From 103b15401933e430f403e61436c581fa8d21cc82 Mon Sep 17 00:00:00 2001 From: Lajos Date: Mon, 6 Nov 2017 14:51:09 +0100 Subject: [PATCH 1/4] ContentTypesExtraction (#71) * [KFI]refactor(ContentTypes): Refactored ContentTypes, extracted type info from class level to a gene * [KFI]refactor(Project): Full LINT * [KFI]fix(BaseHttpProvider): fixed "header is possibly 'undefined'" ts error * [KFI]refactor(ContentTypes): removed unnecessary content type unit tests * [KFI]fix(ContentTypes): fixed User.ImageData type * [KFI]refactor(BinaryField): added fallback to GetDownloadUrl() if the mediaResourceObject has not be * [KFI]refactor(CreateContent, HandleLoadedContent): fixed ContentType parameter passing * [KFI]feat(BatchActions): added ODataBatchResponse model class * [KFI]feat(Repository): improved MoveBatch, added extended response parsing logic * [KFI]feat(Repository): updated CopyBatch action response handling * [KFI]feat(Repository): improved DeleteBatchAction * [KFI]fix(Repository): DeleteBatch parameter * [KFI]test(Repository): Batch operation unit tests * [KFI]fix(Collection): restored ctor parameterization * [KFI]fix(Repository): fixed GetCurrentUser() type * [KFI]test(SchemaTests): removed SchemaTests (logic cleaned up) * [KFI]chore(Collection): removed unnecessary cast * [KFI]fix(ContentTypes): fixed ContentType Binary field type * [KFI]refactor(Schemas, FieldSettings): major refactor Removed logics from the generated Schemas and FieldSettings, cleaned up markup and syntax BREAKING CHANGE: Schemas, FieldSettings * [KFI]refactor(Content, Repository, ControlMapper): moved schema calculation and cache from static Co * [KFI]chore(MockHttpProvider): removed unused _lasUploadOptions * [KFI]fix(ContentReferences): fixed missing generic argument * [KFI]fix(RepositoryEventHub): removed type constraints from event model * [KFI]fix(BinaryField): ContentReference restricted to be a SavedContent * [KFI]fix(UploadModel): removed generic Type parameter from the Parent * [KFI]fix(Repository): changed _loadedContentReferenceCache instance from array to Map * [KFI]fix(ContentInternal): Restricted hierarchy operations to work with SavedContent instances, chan * [KFI]fix(RepositoryEventHub): removed generic type parameter from UploadProgress * [KFI]refactor(ControlMapper): changed control mappings from Array to Map * [KFI]refactor(ODataApi): removed HttpProviderRef reference (unused) * [KFI]fix(ODataHelper): fixed buildUrlParamString parameterization * [KFI]test(BinaryFieldTests): removed unused vars * [KFI]refactor(removed explicit Any type definitions from SavedContent (moved to type declaration)): * [KFI]chore(tsconfig): added skipLibCheck (rxjs strict function definitions error) * [KFI]chore(package): Typescript update * [KFI]test(Fixed test syntaxes): * [KFI]chore(lint & type casting cleanups): * [KFI]fix(ContentSerializer): removed path null check (will be done by the strict type) * [KFI]feat(Collection): added chunked Upload() functionality to Collection * [KFI]test(BinaryField): unit test for fallback DownloadUrl * [KFI]test(Content): Added / improved unit tests for typeguards * [KFI]fix(content): fixed isContent typeguard * [KFI]chore(BinaryField): removed fallback for file name (restricted by SavedContent type) * [KFI]chore(package): bumped version * [KFI]refactor(Schemas): improved isSchema() typeguard and tests * [KFI]test(ODataHelper): fixed ODataHelper tests exports * [KFI]test(FieldSettings): fixed tests exports * [KFI]test(RxHttpProvider): simplified parameterization, improved tests * [KFI]chore(tslint): disabled for-in filter * [KFI]refactor(project): Replaced '@reactivex/rxjs' with the 'rxjs' package * [KFI]chore(ContentReferences cleanup & added missing unit test for search): * [KFI]test(Content): added GetFields() for BinaryField should return the DownloadUrl * [KFI]chore(BaseRepository): cleaned up MoveBatch syntax * [KFI]test(Repository): Added test for get/set Schemas * [KFI]fix(ContentTypes): File.Binary should be mandatory * [KFI]test(Content, Repository): added & improved tests * [KFI]ci(Travis): added Node.JS 9 as a build target * [KFI]chore(package): updated lint --- .travis.yml | 1 + package.json | 16 +- src/Authentication/IAuthenticationService.ts | 7 +- src/Authentication/ITokenPayload.ts | 2 +- src/Authentication/JwtService.ts | 52 +- src/Authentication/LoginResponse.ts | 10 +- src/Authentication/LoginState.ts | 8 +- src/Authentication/RefreshResponse.ts | 6 +- src/Authentication/Token.ts | 18 +- src/Authentication/TokenPersist.ts | 2 +- src/Authentication/TokenStore.ts | 53 +- src/Authentication/TokenStoreType.ts | 12 +- src/BinaryField.ts | 24 +- src/Collection.ts | 174 +- src/ComplexTypes.ts | 68 +- src/Config/snconfigbehavior.ts | 1 - src/Config/snconfigfielddecorator.ts | 2 +- src/Config/snconfigfieldmodelstore.ts | 2 +- src/Config/snconfigmodel.ts | 19 +- .../ContentInternal.ts} | 1293 ++-- src/Content/IContent.ts | 22 + src/Content/ISavedContent.ts | 7 + src/Content/TypeGuards.ts | 40 + src/Content/Types.ts | 7 + src/Content/index.ts | 5 + src/ContentReferences.ts | 108 +- src/ContentSerializer.ts | 25 +- src/ContentTypes.ts | 1795 ++--- src/ControlMapper.ts | 102 +- src/Enums.ts | 2 + src/FieldSettings.ts | 329 +- src/HttpProviders/BaseHttpProvider.ts | 29 +- src/HttpProviders/RxAjaxHttpProvider.ts | 35 +- src/HttpProviders/index.ts | 2 +- src/ODataApi/CustomAction.ts | 26 +- src/ODataApi/ODataApi.ts | 115 +- src/ODataApi/ODataBatchResponse.ts | 13 + src/ODataApi/ODataCollectionResponse.ts | 13 +- src/ODataApi/ODataParams.ts | 36 +- src/ODataApi/ODataRequestOptions.ts | 21 +- src/ODataApi/ODataResponse.ts | 9 +- src/ODataApi/index.ts | 3 +- src/ODataHelper.ts | 97 +- src/Query/Query.ts | 41 +- src/Query/QueryResult.ts | 12 +- src/Query/QuerySegment.ts | 60 +- src/Query/index.ts | 8 +- src/Repository/ActionModel.ts | 18 +- src/Repository/BaseRepository.ts | 433 +- src/Repository/RepositoryEventHub.ts | 99 +- src/Repository/SnRepository.ts | 8 +- src/Repository/UploadModels.ts | 55 +- src/Repository/VersionInfoModels.ts | 13 +- src/Repository/index.ts | 2 +- src/Resources.ts | 3 + src/Retrier.ts | 25 +- src/SN.ts | 61 +- src/Schema/SchemaLoader.ts | 49 + src/Schema/index.ts | 1 + src/Schemas.ts | 6085 +++++++++-------- src/Security.ts | 82 +- test/BinaryFieldTests.ts | 67 +- test/CollectionTests.ts | 75 +- test/ComplexTypesTests.ts | 4 +- test/ContentListReferenceFieldTests.ts | 44 +- test/ContentReferenceFieldTests.ts | 68 +- test/ContentSerializerTests.ts | 28 +- test/ContentTests.ts | 433 +- test/ContentTypeTests.ts | 311 - test/ControlMapperTests.ts | 81 +- test/FieldSettingsTest.ts | 83 +- test/HttpProviderTests.ts | 103 +- test/JwtServiceTests.ts | 142 +- test/Mocks/MockAuthService.ts | 25 +- test/Mocks/MockHttpProvider.ts | 19 +- test/Mocks/MockRepository.ts | 10 +- test/Mocks/MockTokenFactory.ts | 24 +- test/ODataApiTests.ts | 97 +- test/ODataHelperTests.ts | 41 +- test/QueryTests.ts | 152 +- test/RepositoryTests.ts | 529 +- test/RetrierTests.ts | 151 +- test/SchemaTests.ts | 41 - test/SnConfigTests.ts | 1 - test/TokenStoreTests.ts | 27 +- test/TokenTests.ts | 19 +- test/index.ts | 37 +- tsconfig.json | 1 + tslint.json | 103 +- 89 files changed, 6757 insertions(+), 7625 deletions(-) rename src/{Content.ts => Content/ContentInternal.ts} (66%) create mode 100644 src/Content/IContent.ts create mode 100644 src/Content/ISavedContent.ts create mode 100644 src/Content/TypeGuards.ts create mode 100644 src/Content/Types.ts create mode 100644 src/Content/index.ts create mode 100644 src/ODataApi/ODataBatchResponse.ts create mode 100644 src/Schema/SchemaLoader.ts create mode 100644 src/Schema/index.ts delete mode 100644 test/ContentTypeTests.ts delete mode 100644 test/SchemaTests.ts diff --git a/.travis.yml b/.travis.yml index ec523d2..321a276 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ cache: notifications: email: false node_js: + - '9' - '8' - '7' - '6' diff --git a/package.json b/package.json index fce63f3..a72680c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sn-client-js", - "version": "2.5.0", + "version": "3.0.0-development.2", "description": "A JavaScript client for Sense/Net ECM that makes it easy to use the REST API of the Content Repository.", "main": "dist/src/SN.js", "files": [ @@ -11,7 +11,7 @@ "coverage" ], "scripts": { - "lint": "./node_modules/.bin/tslint --project tsconfig.json --type-check", + "lint": "./node_modules/.bin/tslint --project tsconfig.json", "clean": "rimraf dist", "precommit": "npm run lint", "commit": "git-cz", @@ -59,9 +59,9 @@ }, "homepage": "https://sensenet.com", "dependencies": { - "@reactivex/rxjs": "^5.4.2", - "sensenet-kfi-cz-conventional-changelog": "^1.0.0", - "nyc": "^11.0.2" + "nyc": "^11.0.2", + "rxjs": "^5.5.2", + "sensenet-kfi-cz-conventional-changelog": "^1.0.0" }, "devDependencies": { "@types/app-root-path": "1.2.4", @@ -74,11 +74,11 @@ "mocha": "4.0.1", "mocha-typescript": "^1.0.23", "rimraf": "^2.6.1", - "tslint": "^5.4.3", - "tslint-consistent-codestyle": "^1.8.0", + "tslint": "^5.8.0", + "tslint-consistent-codestyle": "^1.9.0", "typedoc": "^0.9.0", "typedoc-plugin-external-module-name": "^1.0.9", - "typescript": "2.5.3" + "typescript": "^2.6.1" }, "config": { "commitizen": { diff --git a/src/Authentication/IAuthenticationService.ts b/src/Authentication/IAuthenticationService.ts index 8c29f6b..eec98de 100644 --- a/src/Authentication/IAuthenticationService.ts +++ b/src/Authentication/IAuthenticationService.ts @@ -2,7 +2,7 @@ * @module Authentication */ /** */ -import { Observable } from '@reactivex/rxjs'; +import { Observable } from 'rxjs/Observable'; import { LoginState } from './'; /** @@ -10,7 +10,8 @@ import { LoginState } from './'; */ export interface IAuthenticationService { /** - * This observable is a public API for subscribing the current state and it's changes. Recommended to work with a private BehaviorSubject in the backgroud. + * This observable is a public API for subscribing the current state and it's changes. + * Recommended to work with a private BehaviorSubject in the backgroud. */ readonly State: Observable; @@ -41,4 +42,4 @@ export interface IAuthenticationService { CurrentUser: string; -} \ No newline at end of file +} diff --git a/src/Authentication/ITokenPayload.ts b/src/Authentication/ITokenPayload.ts index 6f43bae..2b6e3a7 100644 --- a/src/Authentication/ITokenPayload.ts +++ b/src/Authentication/ITokenPayload.ts @@ -40,4 +40,4 @@ export interface ITokenPayload { * name: identifies the name of the user whom the token was issued to */ name: string; -} \ No newline at end of file +} diff --git a/src/Authentication/JwtService.ts b/src/Authentication/JwtService.ts index 1d39464..8cd3af7 100644 --- a/src/Authentication/JwtService.ts +++ b/src/Authentication/JwtService.ts @@ -2,10 +2,13 @@ * @module Authentication */ /** */ -import { LoginState, LoginResponse, RefreshResponse, Token, TokenStore, IAuthenticationService, TokenPersist } from './'; -import { Subject, BehaviorSubject, Observable } from '@reactivex/rxjs'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; import { BaseHttpProvider } from '../HttpProviders/BaseHttpProvider'; import { ODataHelper } from '../SN'; +import { IAuthenticationService, LoginResponse, LoginState, RefreshResponse, Token, + TokenPersist, TokenStore } from './'; /** * This service class manages the JWT authentication, the session and the current login state. @@ -15,16 +18,16 @@ export class JwtService implements IAuthenticationService { private readonly _visitorName: string = 'BuiltIn\\Visitor'; public get CurrentUser(): string { - if (this._tokenStore.AccessToken.IsValid() || this._tokenStore.RefreshToken.IsValid()){ + if (this._tokenStore.AccessToken.IsValid() || this._tokenStore.RefreshToken.IsValid()) { return this._tokenStore.AccessToken.Username || this._tokenStore.RefreshToken.Username; } return this._visitorName; - }; + } /** * This subject indicates the current state of the service * @default LoginState.Pending */ - public get State(): Observable{ + public get State(): Observable { return this._stateSubject.distinctUntilChanged(); } @@ -32,7 +35,7 @@ export class JwtService implements IAuthenticationService { * Gets the current state of the service * @default LoginState.Pending */ - public get CurrentState(): LoginState{ + public get CurrentState(): LoginState { return this._stateSubject.getValue(); } @@ -41,15 +44,15 @@ export class JwtService implements IAuthenticationService { /** * The store for JWT tokens */ - private _tokenStore: TokenStore = new TokenStore(this._repositoryUrl, this._tokenTemplate, (this.Persist === 'session') ? TokenPersist.Session : TokenPersist.Expiration); - + private _tokenStore: TokenStore = + new TokenStore(this._repositoryUrl, this._tokenTemplate, (this.Persist === 'session') ? TokenPersist.Session : TokenPersist.Expiration); /** * Executed before each Ajax call. If the access token has been expired, but the refresh token is still valid, it triggers the token refreshing call * @returns {Observable} An observable with a variable that indicates if there was a refresh triggered. */ public CheckForUpdate(): Observable { - if (this._tokenStore.AccessToken.IsValid()){ + if (this._tokenStore.AccessToken.IsValid()) { this._stateSubject.next(LoginState.Authenticated); return Observable.from([false]); } @@ -66,24 +69,23 @@ export class JwtService implements IAuthenticationService { * @returns {Observable} An observable that will be completed with true on a succesfull refresh */ private execTokenRefresh() { - let refresh = this._httpProviderRef.Ajax(RefreshResponse, { + const refresh = this._httpProviderRef.Ajax(RefreshResponse, { method: 'POST', url: ODataHelper.joinPaths(this._repositoryUrl, 'sn-token/refresh'), headers: { 'X-Refresh-Data': this._tokenStore.RefreshToken.toString(), - 'X-Authentication-Type': 'Token' - } + 'X-Authentication-Type': 'Token', + }, }); - refresh.subscribe(response => { + refresh.subscribe((response) => { this._tokenStore.AccessToken = Token.FromHeadAndPayload(response.access); this._stateSubject.next(LoginState.Authenticated); - }, err => { - console.warn(`There was an error during token refresh: ${err}`); + }, (err) => { this._stateSubject.next(LoginState.Unauthenticated); }); - return refresh.map(response => { return true }); + return refresh.map((response) => true); } /** @@ -101,7 +103,7 @@ export class JwtService implements IAuthenticationService { this._stateSubject = new BehaviorSubject(LoginState.Pending); this.State.subscribe((s) => { - if (this._tokenStore.AccessToken.IsValid()){ + if (this._tokenStore.AccessToken.IsValid()) { this._httpProviderRef.SetGlobalHeader('X-Access-Data', this._tokenStore.AccessToken.toString()); } else { this._httpProviderRef.UnsetGlobalHeader('X-Access-Data'); @@ -142,23 +144,23 @@ export class JwtService implements IAuthenticationService { * ``` */ public Login(username: string, password: string) { - let sub = new Subject(); + const sub = new Subject(); this._stateSubject.next(LoginState.Pending); - let authToken: String = new Buffer(`${username}:${password}`).toString('base64'); + const authToken: string = new Buffer(`${username}:${password}`).toString('base64'); this._httpProviderRef.Ajax(LoginResponse, { method: 'POST', url: ODataHelper.joinPaths(this._repositoryUrl, 'sn-token/login'), headers: { 'X-Authentication-Type': 'Token', - 'Authorization': `Basic ${authToken}` - } + 'Authorization': `Basic ${authToken}`, + }, }) - .subscribe(r => { - let result = this.handleAuthenticationResponse(r); + .subscribe((r) => { + const result = this.handleAuthenticationResponse(r); sub.next(result); - }, err => { + }, (err) => { this._stateSubject.next(LoginState.Unauthenticated); sub.next(false); }); @@ -183,4 +185,4 @@ export class JwtService implements IAuthenticationService { }).map(() => true); } -} \ No newline at end of file +} diff --git a/src/Authentication/LoginResponse.ts b/src/Authentication/LoginResponse.ts index 42592bd..e335837 100644 --- a/src/Authentication/LoginResponse.ts +++ b/src/Authentication/LoginResponse.ts @@ -1,6 +1,8 @@ /** * @module Authentication -*/ /** */ + */ /** */ + +// tslint:disable:naming-convention /** * This class represents a plain response body that is returned from Sense/NET ECM in case of a succesfully login. @@ -9,10 +11,10 @@ export class LoginResponse { /** * The Access Token head and payload in a Base64 encoded format */ - access: string; + public access: string; /** * The Refresh Token head and payload in a Base64 encoded format */ - refresh: string; -} \ No newline at end of file + public refresh: string; +} diff --git a/src/Authentication/LoginState.ts b/src/Authentication/LoginState.ts index 82245d7..523a4a3 100644 --- a/src/Authentication/LoginState.ts +++ b/src/Authentication/LoginState.ts @@ -9,13 +9,13 @@ export enum LoginState { /** * There is a request (login or token refresh) in progress */ - Pending, + Pending = 'Pending', /** * The user is not authenticated */ - Unauthenticated, + Unauthenticated = 'Unauthenticated', /** * The user is authenticated and has a valid access token */ - Authenticated -} \ No newline at end of file + Authenticated = 'Authenticated', +} diff --git a/src/Authentication/RefreshResponse.ts b/src/Authentication/RefreshResponse.ts index e667cb5..32c98bc 100644 --- a/src/Authentication/RefreshResponse.ts +++ b/src/Authentication/RefreshResponse.ts @@ -2,6 +2,8 @@ * @module Authentication */ /** */ +// tslint:disable:naming-convention + /** * This class represents a plain response body that is returned from Sense/NET ECM in case of a succesfully login. */ @@ -9,5 +11,5 @@ export class RefreshResponse { /** * The Access Token head and payload in a Base64 encoded format */ - access: string; -} \ No newline at end of file + public access: string; +} diff --git a/src/Authentication/Token.ts b/src/Authentication/Token.ts index 6e716f0..b924d12 100644 --- a/src/Authentication/Token.ts +++ b/src/Authentication/Token.ts @@ -19,13 +19,13 @@ export class Token { iss: '', name: '', nbf: 0, - sub: '' - } + sub: '', + }; } - }; + } private fromEpoch(epoch: number): Date { - let d = new Date(0); + const d = new Date(0); d.setUTCSeconds(epoch); return d; } @@ -35,7 +35,7 @@ export class Token { */ public get Username(): string { return this._tokenPayload.name; - }; + } /** * The current Token full Payload @@ -62,7 +62,7 @@ export class Token { * Indicates if the Token is valid based on it's ExpirationTime and NotBefore values. */ public IsValid(): boolean { - let now = new Date(); + const now = new Date(); return this._tokenPayload && this.ExpirationTime > now && this.NotBefore < now; } @@ -76,11 +76,11 @@ export class Token { /** * Returns the Token in string format (in a base64 encoded, dot separated header and payload) */ + // tslint:disable-next-line:naming-convention public toString() { return `${this._headerEncoded}.${this._payloadEncoded}`; } - /** * Factory method to create a token from a sense NET specific base64 encoded header and payload string, e.g.: * ``` @@ -89,8 +89,8 @@ export class Token { * @constructs Token */ public static FromHeadAndPayload(headAndPayload: string): Token { - let [head, payload] = headAndPayload.split('.'); - return new Token(head, payload) + const [head, payload] = headAndPayload.split('.'); + return new Token(head, payload); } /** diff --git a/src/Authentication/TokenPersist.ts b/src/Authentication/TokenPersist.ts index 488f683..fc70aef 100644 --- a/src/Authentication/TokenPersist.ts +++ b/src/Authentication/TokenPersist.ts @@ -14,4 +14,4 @@ export enum TokenPersist { * Token should be removed when the token will be expired */ Expiration -} \ No newline at end of file +} diff --git a/src/Authentication/TokenStore.ts b/src/Authentication/TokenStore.ts index 1926a64..e1d613b 100644 --- a/src/Authentication/TokenStore.ts +++ b/src/Authentication/TokenStore.ts @@ -8,28 +8,27 @@ import { Token, TokenPersist, TokenStoreType } from './'; */ export type TokenType = 'access' | 'refresh'; - /** * This class is intended to store token data in LocalStorage or in-memory storage. */ export class TokenStore { /** - * @param {strnig} baseUrl The Base URL to the related site - * @param {string} keyTemplate The template to use when generating keys in the local/session storage or for a cookie. ${siteName} and ${tokenName} will be replaced. Example: 'sn-${siteName}-${tokenName}' - * @param {TokenPersist} tokenPersist Setting that indicates if the token should be persisted per session (browser close) or per Token expiration (based on the token `exp` property) - * @param {Partial} documentRef The Document reference (used by unit tests) - * @param {Storage} localStorageRef The localStorage reference (used by unit tests) - * @param {Storage} sessionStorageRef The sessionStorage reference (used by unit tests) + * @param {strnig} baseUrl The Base URL to the related site + * @param {string} keyTemplate The template to use when generating keys in the local/session storage or for a cookie. ${siteName} and ${tokenName} will be replaced. Example: 'sn-${siteName}-${tokenName}' + * @param {TokenPersist} tokenPersist Setting that indicates if the token should be persisted per session (browser close) or per Token expiration (based on the token `exp` property) + * @param {Partial} documentRef The Document reference (used by unit tests) + * @param {Storage} localStorageRef The localStorage reference (used by unit tests) + * @param {Storage} sessionStorageRef The sessionStorage reference (used by unit tests) */ constructor(private readonly _baseUrl: string, - private readonly _keyTemplate: string, - private readonly _tokenPersist: TokenPersist, - private _documentRef = (typeof document === 'object') ? document : undefined, - private _localStorageRef = (typeof localStorage === 'object') ? localStorage : undefined, - private _sessionStorageRef = (typeof sessionStorage === 'object') ? sessionStorage : undefined) { - let storesAvailable = (typeof this._localStorageRef !== 'undefined' && typeof this._sessionStorageRef !== 'undefined'); - let cookieAvailable = (typeof this._documentRef !== 'undefined' && typeof this._documentRef.cookie !== 'undefined'); + private readonly _keyTemplate: string, + private readonly _tokenPersist: TokenPersist, + private _documentRef = (typeof document === 'object') ? document : undefined, + private _localStorageRef = (typeof localStorage === 'object') ? localStorage : undefined, + private _sessionStorageRef = (typeof sessionStorage === 'object') ? sessionStorage : undefined) { + const storesAvailable = (typeof this._localStorageRef !== 'undefined' && typeof this._sessionStorageRef !== 'undefined'); + const cookieAvailable = (typeof this._documentRef !== 'undefined' && typeof this._documentRef.cookie !== 'undefined'); if (!storesAvailable && !cookieAvailable) { this.TokenStoreType = TokenStoreType.InMemory; @@ -56,21 +55,21 @@ export class TokenStore { private getTokenFromCookie(key: string, document: Document): Token { const prefix = key + '='; - if (document && document.cookie){ + if (document && document.cookie) { const cookieVal = document.cookie.split(';') - .map(v => v.trim()) - .find(v => v.trim().indexOf(prefix) === 0); - if (cookieVal){ + .map((v) => v.trim()) + .find((v) => v.trim().indexOf(prefix) === 0); + if (cookieVal) { return Token.FromHeadAndPayload(cookieVal.substring(prefix.length)); } } return Token.CreateEmpty(); } - private setTokenToCookie(key: string, Token: Token, persist: TokenPersist, doc: Document): void { - let cookie = `${key}=${Token.toString()}`; + private setTokenToCookie(key: string, token: Token, persist: TokenPersist, doc: Document): void { + let cookie = `${key}=${token.toString()}`; if (persist === TokenPersist.Expiration) { - cookie += `; expires=${Token.ExpirationTime.toUTCString()};` + cookie += `; expires=${token.ExpirationTime.toUTCString()};`; } doc.cookie = cookie; } @@ -85,7 +84,7 @@ export class TokenStore { try { switch (this.TokenStoreType) { case TokenStoreType.InMemory: - return Token.FromHeadAndPayload(this._innerStore[storeKey as any]); + return Token.FromHeadAndPayload(this._innerStore[storeKey]); case TokenStoreType.LocalStorage: return Token.FromHeadAndPayload((this._localStorageRef as any).getItem(storeKey)); case TokenStoreType.SessionStorage: @@ -107,16 +106,16 @@ export class TokenStore { */ public SetToken(key: TokenType, token: Token) { const storeKey = this.getStoreKey(key); - let dtaString = token.toString(); + const dtaString = token.toString(); switch (this.TokenStoreType) { case TokenStoreType.InMemory: - this._innerStore[storeKey as any] = dtaString; + this._innerStore[storeKey] = dtaString; break; case TokenStoreType.LocalStorage: - this._localStorageRef && this._localStorageRef.setItem(storeKey, dtaString); + (this._localStorageRef as Storage).setItem(storeKey, dtaString); break; case TokenStoreType.SessionStorage: - this._sessionStorageRef && this._sessionStorageRef.setItem(storeKey, dtaString); + (this._sessionStorageRef as Storage).setItem(storeKey, dtaString); break; case TokenStoreType.ExpirationCookie: this.setTokenToCookie(storeKey, token, TokenPersist.Expiration, this._documentRef as Document); @@ -147,4 +146,4 @@ export class TokenStore { this.SetToken('refresh', value); } -} \ No newline at end of file +} diff --git a/src/Authentication/TokenStoreType.ts b/src/Authentication/TokenStoreType.ts index 3997fcd..584f418 100644 --- a/src/Authentication/TokenStoreType.ts +++ b/src/Authentication/TokenStoreType.ts @@ -10,24 +10,24 @@ export enum TokenStoreType { /** * The is stored in a cookie, without expiration date (Session Cookie). */ - SessionCookie, + SessionCookie = 'SessionCookie', /** * The token is stored in a cookie, the expiration dates will match the token expiration dates */ - ExpirationCookie, + ExpirationCookie = 'ExpirationCookie', /** * The token is stored in the local sessionStorage */ - SessionStorage, + SessionStorage = 'SessionStorage', /** * The token is stored in the localStorage */ - LocalStorage, + LocalStorage = 'LocalStorage', /** * The token is stored in an in-memory storage (fallback) */ - InMemory -} \ No newline at end of file + InMemory = 'InMemory' +} diff --git a/src/BinaryField.ts b/src/BinaryField.ts index 3015d7d..dc5538a 100644 --- a/src/BinaryField.ts +++ b/src/BinaryField.ts @@ -2,17 +2,16 @@ * @module Content */ /** */ -import { Content, SavedContent } from './Content'; +import { Observable } from 'rxjs/Observable'; import { MediaResourceObject } from './ComplexTypes'; -import { Observable } from '@reactivex/rxjs'; -import { UploadProgressInfo } from './Repository/UploadModels'; +import { IContent, SavedContent } from './Content'; import { BinaryFieldSetting } from './FieldSettings'; +import { UploadProgressInfo } from './Repository/UploadModels'; /** * Represents a binary field instance */ -export class BinaryField> { - +export class BinaryField { /** * Saves a File object instance (from a form input or drop event) into the binary field @@ -23,8 +22,8 @@ export class BinaryField> { = (file: File) => this._contentReference.GetRepository().UploadFile({ - File: new File([file], this._contentReference.Name || file.name), - Parent: {GetFullPath: () => this._contentReference.ParentContentPath, Path: this._contentReference.ParentPath}, + File: new File([file], this._contentReference.Name), + Parent: {GetFullPath: () => this._contentReference.ParentContentPath, Path: this._contentReference.ParentPath} as SavedContent, PropertyName: this._fieldSettings.Name, ContentType: this._contentReference.constructor as { new(...args: any[]): T }, Overwrite: true, @@ -35,12 +34,15 @@ export class BinaryField> { * @param {string} text The text to be saved * @returns {Observable>} An observable that will update with the upload progress */ - public SaveBinaryText: (text: string) => Observable> = (text: string) => this.SaveBinaryFile(new File([text], this._contentReference.Name || 'File')); + public SaveBinaryText: (text: string) => Observable> = (text: string) => this.SaveBinaryFile(new File([text], this._contentReference.Name)); /** * Returns the download URL for the binary */ public GetDownloadUrl(): string { + if (!this._mediaResourceObject || typeof this._mediaResourceObject !== 'object') { + return `/binaryhandler.ashx?nodeid=${this._contentReference.Id}&propertyname=${this._fieldSettings.Name}`; + } return this._mediaResourceObject.__mediaresource.media_src; } @@ -58,7 +60,7 @@ export class BinaryField> { * @param {BinaryFieldSetting} _fieldSettings The corresponding fieldsettings */ constructor(private readonly _mediaResourceObject: MediaResourceObject, - private readonly _contentReference: T, - private readonly _fieldSettings: BinaryFieldSetting) { + private readonly _contentReference: SavedContent, + private readonly _fieldSettings: BinaryFieldSetting) { } -} \ No newline at end of file +} diff --git a/src/Collection.ts b/src/Collection.ts index 9771f1e..d5db2db 100644 --- a/src/Collection.ts +++ b/src/Collection.ts @@ -7,26 +7,29 @@ * a container ```Content``` with a type ```Folder``` or ```ContentList```, it could be also a result of a query. Since it is ```generic``` type of its children items is not defined strictly. */ /** */ -import { Observable } from '@reactivex/rxjs'; -import { CustomAction, IODataParams, IODataRequestOptions, ODataApi } from './ODataApi'; -import { ODataHelper } from './SN'; -import { Content } from './Content'; -import { BaseRepository } from './Repository/BaseRepository'; -import { BaseHttpProvider } from './HttpProviders/BaseHttpProvider'; + import { Observable } from 'rxjs/Observable'; + import { Content, IContent } from './Content'; + import { CustomAction, IODataParams, IODataRequestOptions } from './ODataApi'; + import { BaseRepository } from './Repository/BaseRepository'; + import { UploadProgressInfo, UploadTextOptions, WithParentContent } from './Repository/UploadModels'; + import { ODataHelper } from './SN'; -export class Collection { - odata: ODataApi; - Path: string = ''; + export class Collection { + public Path: string = ''; + + private get _odata() { + return this._repository.GetODataApi(); + } /** - * @constructs Collection - * @param {T[]} items An array that holds items. - * @param { IODataApi } service The service to use as API Endpoint - */ - constructor(private _items: T[], - private _repository: BaseRepository, - private readonly _contentType: { new(...args: any[]): T } = Content as { new(...args: any[]): any }) { - this.odata = _repository.GetODataApi(); + * @constructs Collection + * @param {T[]} items An array that holds items. + * @param { IODataApi } service The service to use as API Endpoint + */ + constructor(private _items: Content[], + private _repository: BaseRepository, + contentType: { new(...args: any[]): T } + ) { } /** @@ -49,7 +52,7 @@ export class Collection { * ``` */ public Item(id: number): T | undefined { - return this._items.find(i => i.Id === id); + return this._items.find((i) => i.Id === id); } /** @@ -63,8 +66,8 @@ export class Collection { return this._items.length; } /** - * Method to add an item to a local collection and to the Content Repository through OData REST API at the same time. - * + * Method to add an item to a local collection and to the Content Repository through OData REST API at the same time. + * * Calls the method [CreateContent]{@link ODataApi.CreateContent} with the current collections path and the given content as parameters. * @param content {Content} The item that has to be saved. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. @@ -80,10 +83,10 @@ export class Collection { * }); * ``` */ - public Add(content: T['options']): Observable { - const newcontent = this.odata.Post(this.Path, content, this._contentType) - .map(resp => { - return this._repository.HandleLoadedContent(resp as any, this._contentType); + public Add(content: Content): Observable> { + const newcontent = this._odata.Post(this.Path, content.GetFields()) + .map((resp) => { + return this._repository.HandleLoadedContent(resp); }); newcontent .subscribe({ @@ -136,23 +139,22 @@ export class Collection { */ public Remove(arg: number | number[], permanently: boolean = false): Observable { if (typeof arg === 'number') { - let content = this._items[arg]; + const content = this._items[arg]; if (content && content.Id) { this._items = this._items.slice(0, arg) .concat(this._items.slice(arg + 1)); - return this.odata.Delete(content.Id, permanently ? permanently : false); + return this._odata.Delete(content.Id, permanently ? permanently : false); } else { return Observable.of(undefined); } - } - else { - let ids = arg.map(i => this._items[i].Id); + } else { + const ids = arg.map((i) => this._items[i].Id); this._items = this._items.filter((item, i) => arg.indexOf(i) > -1); - let action = new CustomAction({ name: 'DeleteBatch', path: this.Path, isAction: true, requiredParams: ['paths'] }); - return this.odata.CreateCustomAction(action, { data: [{ 'paths': ids }, { 'permanently': permanently }] }); + const action = new CustomAction({ name: 'DeleteBatch', path: this.Path, isAction: true, requiredParams: ['paths'] }); + return this._odata.CreateCustomAction(action, { data: [{ paths: ids }, { permanently }] }); } } /** @@ -179,12 +181,13 @@ export class Collection { */ public Read(path: string, options?: IODataParams): Observable { this.Path = path; - const children = this.odata.Fetch({ + const children = this._odata.Fetch({ params: options, - path: path + path }) - .map(items => { - return items.d.results.map(c => this._repository.HandleLoadedContent(c as any, this._contentType)); + .map((items) => { + return items.d.results.map((c) => + this._repository.HandleLoadedContent(c)); }); return children; } @@ -227,15 +230,14 @@ export class Collection { this._items = this._items.slice(0, arg) .concat(this._items.slice(arg + 1)); - let action = new CustomAction({ name: 'Move', id: arg, isAction: true, requiredParams: ['targetPath'] }); - return this.odata.CreateCustomAction(action, { data: [{ 'targetPath': targetPath }] }); - } - else { - let ids = arg.map(i => this._items[i].Id); + const action = new CustomAction({ name: 'Move', id: arg, isAction: true, requiredParams: ['targetPath'] }); + return this._odata.CreateCustomAction(action, { data: [{ targetPath }] }); + } else { + const ids = arg.map((i) => this._items[i].Id); this._items = this._items.filter((item, i) => arg.indexOf(i) > -1); - let action = new CustomAction({ name: 'MoveBatch', path: this.Path, isAction: true, requiredParams: ['paths', 'targetPath'] }); - return this.odata.CreateCustomAction(action, { data: [{ 'paths': ids, 'targetPath': targetPath }] }); + const action = new CustomAction({ name: 'MoveBatch', path: this.Path, isAction: true, requiredParams: ['paths', 'targetPath'] }); + return this._odata.CreateCustomAction(action, { data: [{ paths: ids, targetPath }] }); } } /** @@ -257,31 +259,30 @@ export class Collection { */ /** - * Method to copy multiple content to another container. - * @param items {number[]} number array of content indexes. - * @params targetPath {string} Path of the target container. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let copy = myCollection.Copy([3, 5], '/Root/MyContent/MyFolder'); - * copy - * .subscribe({ - * next: response => { - * //do something after copy - * }, - * error: error => console.error('something wrong occurred: ' + error), - * complete: () => console.log('done'), - * }); - * ``` - */ + * Method to copy multiple content to another container. + * @param items {number[]} number array of content indexes. + * @params targetPath {string} Path of the target container. + * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * ``` + * let copy = myCollection.Copy([3, 5], '/Root/MyContent/MyFolder'); + * copy + * .subscribe({ + * next: response => { + * //do something after copy + * }, + * error: error => console.error('something wrong occurred: ' + error), + * complete: () => console.log('done'), + * }); + * ``` + */ public Copy(arg: number | number[], targetPath: string): Observable { if (typeof arg === 'number') { - let action = new CustomAction({ name: 'Copy', id: arg, isAction: true, requiredParams: ['targetPath'] }); - return this.odata.CreateCustomAction(action, { data: [{ 'targetPath': targetPath }] }); - } - else { - let ids = arg.map(i => this._items[i].Id); - let action = new CustomAction({ name: 'CopyBatch', path: this.Path, isAction: true, requiredParams: ['paths', 'targetPath'] }); - return this.odata.CreateCustomAction(action, { data: [{ 'paths': ids, 'targetPath': targetPath }] }); + const action = new CustomAction({ name: 'Copy', id: arg, isAction: true, requiredParams: ['targetPath'] }); + return this._odata.CreateCustomAction(action, { data: [{ targetPath }] }); + } else { + const ids = arg.map((i) => this._items[i].Id); + const action = new CustomAction({ name: 'CopyBatch', path: this.Path, isAction: true, requiredParams: ['paths', 'targetPath'] }); + return this._odata.CreateCustomAction(action, { data: [{ paths: ids, targetPath }] }); } } /** @@ -299,14 +300,14 @@ export class Collection { * }); * ``` */ - public AllowedChildTypes(options?: Object): Observable { - let o: any = {}; + public AllowedChildTypes(options?: any): Observable { + const o: any = {}; if (options) { - o['params'] = options; + o.params = options; } - o['path'] = ODataHelper.getContentURLbyPath(this.Path); - let optionList = o as IODataRequestOptions; - return this.odata.Get(optionList); + o.path = ODataHelper.getContentURLbyPath(this.Path); + const optionList = o as IODataRequestOptions; + return this._odata.Get(optionList); } /** * Uploads a stream or text to a content binary field (e.g. a file). @@ -323,32 +324,11 @@ export class Collection { * @params {FileText} In case you do not have the file as a real file in the file system but a text in the browser, you can provide the raw text in this parameter. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. */ - public Upload(contentType: string, fileName: string, overwrite: boolean = true, useChunk: boolean = false, propertyName?: string, fileText?: string): Observable { - const data: any = { - ContentType: contentType, - FileName: fileName, - Overwrite: overwrite, - UseChunk: useChunk - }; - if (typeof propertyName !== 'undefined') { - data['PropertyName'] = propertyName; - } - if (typeof fileText !== 'undefined') { - data['FileText'] = fileText; - } - let uploadCreation = this.odata.Upload(this.Path, data, true); - uploadCreation.subscribe({ - next: (response) => { - const data = { - ContentType: contentType, - FileName: fileName, - Overwrite: overwrite, - ChunkToken: response - }; - return this.odata.Upload(this.Path, data, false); - } + public Upload(options: WithParentContent> ) { + const uploadRequest = this._repository.UploadTextAsFile(options); + uploadRequest.first().subscribe((progress) => { + this._items.push((progress as UploadProgressInfo).CreatedContent); }); - return uploadCreation; + return uploadRequest; } } - diff --git a/src/ComplexTypes.ts b/src/ComplexTypes.ts index 0947c4f..94b72f2 100644 --- a/src/ComplexTypes.ts +++ b/src/ComplexTypes.ts @@ -2,34 +2,36 @@ * @module ComplexTypes * @preferred * -* @description Module containing complex data types like HyperLink or ChoiceOption. -* -* This module is autogenerated from Sense/Net ContentRepository. -* -* ``` -* let link = new Fields.HyperlinkData({ -* Href: 'http://sensenet.com', -* Text: 'Link to sensenet.com', -* Title: 'Go to sensenet.com', -* Target: '_blank' -* }); -* -* let webContent = new ContentTypes.WebContentDemo({ -* Id: 1, -* Name: 'MyContent', -* DisplayName: 'My Content', -* Type: 'WebContentDemo', -* Details: link -* }); -* -* ``` -*/ /** */ + * @description Module containing complex data types like HyperLink or ChoiceOption. + * + * This module is autogenerated from Sense/Net ContentRepository. + * + * ``` + * let link = new Fields.HyperlinkData({ + * Href: 'http://sensenet.com', + * Text: 'Link to sensenet.com', + * Title: 'Go to sensenet.com', + * Target: '_blank' + * }); + * + * let webContent = new ContentTypes.WebContentDemo({ + * Id: 1, + * Name: 'MyContent', + * DisplayName: 'My Content', + * Type: 'WebContentDemo', + * Details: link + * }); + * + * ``` + */ /** */ + +// tslint:disable:naming-convention export class ChoiceOption { - Value: string; - Text?: string; - Enabled?: boolean; - Selected?: boolean; + public Value: string; + public Text?: string; + public Enabled?: boolean; + public Selected?: boolean; constructor(value: string, text?: string, enabled?: boolean, selected?: boolean) { this.Value = value; @@ -40,20 +42,20 @@ export class ChoiceOption { } export class DeferredUriObject { - uri: string; + public uri: string; } export class DeferredObject extends Object { - __deferred: DeferredUriObject; + public __deferred: DeferredUriObject; } export class MediaObject { - edit_media: string; - media_src: string; - content_type: string; - media_etag: string; + public edit_media: string; + public media_src: string; + public content_type: string; + public media_etag: string; } export class MediaResourceObject extends Object { - __mediaresource: MediaObject; + public __mediaresource: MediaObject; } diff --git a/src/Config/snconfigbehavior.ts b/src/Config/snconfigbehavior.ts index 5ebd3d1..49d0f05 100644 --- a/src/Config/snconfigbehavior.ts +++ b/src/Config/snconfigbehavior.ts @@ -2,7 +2,6 @@ * @module Config *//** */ - /** * This enum represents an SnConfig field behavior. Values can be used as flags. */ diff --git a/src/Config/snconfigfielddecorator.ts b/src/Config/snconfigfielddecorator.ts index bc160c6..c8944ac 100644 --- a/src/Config/snconfigfielddecorator.ts +++ b/src/Config/snconfigfielddecorator.ts @@ -17,4 +17,4 @@ export const SnConfigField = (model: SnConfigFieldModel) => { model.StoreKey = `${target.constructor.name}.${propertyName}`; SnConfigFieldModelStore.Add(model); }; -} +}; diff --git a/src/Config/snconfigfieldmodelstore.ts b/src/Config/snconfigfieldmodelstore.ts index 961576b..7cd8cca 100644 --- a/src/Config/snconfigfieldmodelstore.ts +++ b/src/Config/snconfigfieldmodelstore.ts @@ -21,7 +21,7 @@ export class SnConfigFieldModelStore { * @throws error if a field with the same name already exists */ public static Add(newModel: SnConfigFieldModel) { - if (!newModel.StoreKey){ + if (!newModel.StoreKey) { throw Error('No Store key defined'); } if (this.Contains(newModel.StoreKey)) { diff --git a/src/Config/snconfigmodel.ts b/src/Config/snconfigmodel.ts index 73dcc2a..9d5fc5b 100644 --- a/src/Config/snconfigmodel.ts +++ b/src/Config/snconfigmodel.ts @@ -2,10 +2,10 @@ * @module Config *//** */ +import { Content } from '../Content'; +import { ODataFieldParameter, ODataMetadataType } from '../ODataApi/ODataParams'; import { SnConfigBehavior } from './snconfigbehavior'; import { SnConfigField } from './snconfigfielddecorator'; -import { ODataMetadataType, ODataFieldParameter } from '../ODataApi/ODataParams'; -import { Content } from '../Content'; /** * Class that represents a typed model for the Sense/Net related configuration for an NPM Package. The values can be populated from sn.config.js. @@ -16,8 +16,9 @@ export class SnConfigModel { * The default base URL, returns window.location if available */ public static get DEFAULT_BASE_URL(): string { - if (typeof window !== 'undefined') + if (typeof window !== 'undefined') { return (window && window.location && window.location.origin) || ''; + } return ''; } @@ -26,7 +27,6 @@ export class SnConfigModel { */ public static readonly DEFAULT_SERVICE_TOKEN: string = 'odata.svc'; - /** * The root URL for the Sense/Net repository (e.g.: demo.sensenet.com) */ @@ -45,6 +45,7 @@ export class SnConfigModel { FieldDescription: 'The service token for the OData Endpoint', Question: 'Please enter your Sense/Net Site URL(e.g.:demo.sensenet.com):', }) + // tslint:disable-next-line:naming-convention public ODataToken: string = SnConfigModel.DEFAULT_SERVICE_TOKEN; /** @@ -57,7 +58,6 @@ export class SnConfigModel { }) public JwtTokenKeyTemplate: string = 'sn-${siteName}-${tokenName}'; - /** * This string describes how long the JWT token should be persisted. */ @@ -76,8 +76,7 @@ export class SnConfigModel { FieldDescription: 'The default values to select when triggering an OData Action', Question: '' }) - public DefaultSelect: ODataFieldParameter | 'all' = ['DisplayName', 'Description', 'Icon'] - + public DefaultSelect: ODataFieldParameter | 'all' = ['DisplayName', 'Description', 'Icon']; /** * This parameter describes what fields should always be included in the OData $select statements @@ -87,7 +86,7 @@ export class SnConfigModel { FieldDescription: 'The values are required when triggering an OData Action and will be always included in Select statements', Question: '' }) - public RequiredSelect: ODataFieldParameter = ['Id', 'Path', 'Name', 'Type'] + public RequiredSelect: ODataFieldParameter = ['Id', 'Path', 'Name', 'Type']; /** * This field sets the default OData $metadata value @@ -107,7 +106,7 @@ export class SnConfigModel { FieldDescription: 'The default OData inline count option', Question: '' }) - public DefaultInlineCount: 'allpages' | 'none' = 'allpages' + public DefaultInlineCount: 'allpages' | 'none' = 'allpages'; /** * This field describes what fields should be expanded on every OData request by default @@ -146,7 +145,7 @@ export class SnConfigModel { */ constructor(config?: Partial) { if (config) { - for (let key in config) { + for (const key in config) { if (config[key]) { this[key] = config[key]; } diff --git a/src/Content.ts b/src/Content/ContentInternal.ts similarity index 66% rename from src/Content.ts rename to src/Content/ContentInternal.ts index ca2c1d8..4d449ec 100644 --- a/src/Content.ts +++ b/src/Content/ContentInternal.ts @@ -41,82 +41,53 @@ * related to async data streams. */ /** */ -import { Schemas, Security, Enums, ODataHelper, FieldSettings, ContentTypes } from './SN'; -import { IODataParams, ODataApi, ODataFieldParameter } from './ODataApi'; -import { Observable } from '@reactivex/rxjs'; -import { ActionModel } from './Repository/ActionModel'; -import { BaseRepository } from './Repository/BaseRepository'; -import { BaseHttpProvider } from './HttpProviders/BaseHttpProvider'; -import { ContentSerializer } from './ContentSerializer'; -import { DeferredObject } from './ComplexTypes'; -import { ContentListReferenceField, ContentReferenceField } from './ContentReferences'; -import { Workspace, User, ContentType, GenericContent, Group } from './ContentTypes'; -import { QueryExpression, QuerySegment, FinializedQuery } from './Query'; -import { BinaryField } from './BinaryField'; -import { UploadFileOptions, UploadProgressInfo, UploadTextOptions, UploadFromEventOptions } from './Repository/UploadModels'; - -/** - * Typeguard that determines if the specified Object is a DeferredObject - * @param fieldObject The object that needs to be checked - */ -export const isDeferred = (fieldObject: any): fieldObject is DeferredObject => { - return fieldObject && fieldObject.__deferred && fieldObject.__deferred.uri && fieldObject.__deferred.uri.length > 0 || false; -} - -/** - * Typeguard that determines if the specified Object is an IContentOptions instance - * @param object The object that needs to be checked - */ -export const isContentOptions = (object: any): object is IContentOptions => { - return object && object.Id && object.Path && object.Type && object.Type.length > 0 || false; -} - -/** - * Typeguard that determines if the specified Object is a Content instance - * @param object The object that needs to be checked - */ -export const isContent = (object: any): object is Content => { - return object && object.Id && object.Path && object.Type && object.Type.length > 0 && object.options && isContentOptions(object.options) || false; -} - -/** - * Typeguard that determines if the specified Object is an IContentOptions array - * @param {any[]} objectList The object that needs to be checked - */ -export const isContentOptionList = (objectList: any[]): objectList is IContentOptions[] => { - return objectList && objectList.length !== undefined && objectList.find(o => !isContentOptions(o)) === undefined || false; -} - - -export type SavedContent = T & { Id: number, Path: string }; - -export class Content { + import { Observable } from 'rxjs/Observable'; + import { BinaryField } from '../BinaryField'; + import { ContentListReferenceField, ContentReferenceField } from '../ContentReferences'; + import { ContentSerializer } from '../ContentSerializer'; + import { ContentType, Group, User, Workspace } from '../ContentTypes'; + import { BaseHttpProvider } from '../HttpProviders/BaseHttpProvider'; + import { IODataParams, ODataApi, ODataFieldParameter } from '../ODataApi'; + import { FinializedQuery, QueryExpression, QuerySegment } from '../Query'; + import { ActionModel } from '../Repository/ActionModel'; + import { BaseRepository } from '../Repository/BaseRepository'; + import { UploadFileOptions, UploadFromEventOptions, UploadProgressInfo, UploadTextOptions } from '../Repository/UploadModels'; + import { ContentTypes, Enums, FieldSettings, ODataHelper, Schemas, Security } from '../SN'; + import { IContent } from './IContent'; + import { ISavedContent } from './ISavedContent'; + import { isSavedContent } from './TypeGuards'; + import { Content, SavedContent } from './Types'; + + export class ContentInternal { private get _odata(): ODataApi { return this._repository.GetODataApi(); } - /** - * An unique identifier for a content - */ - Id?: number; - - /** - * Name of the Content - */ - Name?: string; - - private _type?: string; + private _type: string = this._contentType.name; /** * Type of the Content, e.g.: 'Task' or 'User' */ public get Type(): string { - return this._type || this.constructor.name; + return this._type; } public set Type(newType: string) { this._type = newType; } + public Id?: number; + public Path?: string; + public Name?: string; + public ParentId?: number; + public Versions: ContentListReferenceField; + public Workspace: ContentReferenceField; + public Owner: ContentReferenceField; + public CreatedBy: ContentReferenceField; + public ModifiedBy: ContentReferenceField; + public CheckedOutTo: ContentReferenceField; + public EffectiveAllowedChildTypes: ContentListReferenceField; + public AllowedChildTypes: ContentListReferenceField; + private _isSaved: boolean = false; /** @@ -126,7 +97,6 @@ export class Content { return this._isSaved; } - /** * Returns the assigned Repository instance */ @@ -155,20 +125,23 @@ export class Content { public GetChanges(): T { const changedFields: T = {} as T; - for (let field in this.GetFields()) { - const currentField = (this as any)[field]; + // tslint:disable-next-line:forin + for (const field in this.GetFields()) { + const currentField = this[field as any]; if (currentField !== this._lastSavedFields[field]) { if (currentField instanceof ContentReferenceField) { if (currentField.IsDirty) { - changedFields[field] = currentField.getValue(); + changedFields[field] = currentField.GetValue(); } } else if (currentField instanceof ContentListReferenceField) { if (currentField.IsDirty) { - changedFields[field] = currentField.getValue(); + changedFields[field] = currentField.GetValue(); } + } else if (currentField instanceof BinaryField) { + /* skip, binaries cannot be compared */ } else { - changedFields[field] = (this as any)[field] + changedFields[field] = this[field as any]; } } } @@ -180,14 +153,23 @@ export class Content { */ public GetFields(skipEmpties?: boolean): T { const fieldsToPost = {} as T; - this.GetSchema().FieldSettings.forEach(s => { - const value = this[s.Name] && this[s.Name].getValue ? this[s.Name].getValue() : this[s.Name]; - ((!skipEmpties && value !== undefined) || (skipEmpties && value)) && (fieldsToPost[s.Name] = value); + this.GetSchema().FieldSettings.forEach((s) => { + let value = this[s.Name]; + if (this[s.Name] && this[s.Name].GetValue) { + value = this[s.Name].GetValue(); + } + + if (this[s.Name] && (this[s.Name] as BinaryField).GetDownloadUrl) { + value = (this[s.Name] as BinaryField).GetDownloadUrl(); + } + + if ((!skipEmpties && value !== undefined) || (skipEmpties && value)) { + fieldsToPost[s.Name] = value; + } }); return fieldsToPost; } - /** * Shows if the content has been changed on client-side since the last load */ @@ -210,51 +192,39 @@ export class Content { public get IsValid(): boolean { const schema = this.GetSchema(); const missings = schema.FieldSettings - .filter(s => s.Compulsory && !(this as any)[s.Name]) + .filter((s) => s.Compulsory && !(this as any)[s.Name]); return missings.length === 0; } - DisplayName?: string; - Description?: string; - Icon?: string; - IsFolder?: boolean; - Path?: string; - Index?: number; - CreationDate?: string; - ModificationDate?: string; - ParentId?: number; - Versions: ContentListReferenceField; - Workspace: ContentReferenceField; - Owner: ContentReferenceField; - CreatedBy: ContentReferenceField; - ModifiedBy: ContentReferenceField; - CheckedOutTo: ContentReferenceField; - - EffectiveAllowedChildTypes: ContentListReferenceField; - AllowedChildTypes: ContentListReferenceField; - - private _fieldHandlerCache: (ContentListReferenceField | ContentReferenceField)[] = [] + private _fieldHandlerCache: (ContentListReferenceField | ContentReferenceField)[] = []; private updateReferenceFields() { - const referenceSettings: FieldSettings.ReferenceFieldSetting[] = this.GetSchema().FieldSettings.filter(f => f instanceof FieldSettings.ReferenceFieldSetting); - referenceSettings.push(...[{ Name: 'EffectiveAllowedChildTypes', AllowMultiple: true }, { Name: 'AllowedChildTypes', AllowMultiple: true }]); - referenceSettings.forEach(f => { + const referenceSettings: FieldSettings.ReferenceFieldSetting[] = this.GetSchema().FieldSettings.filter((f) => FieldSettings.isFieldSettingOfType(f, FieldSettings.ReferenceFieldSetting)); + referenceSettings.push(...[{ Type: 'ReferenceFieldSetting', Name: 'EffectiveAllowedChildTypes', AllowMultiple: true }, { Type: 'ReferenceFieldSetting', Name: 'AllowedChildTypes', AllowMultiple: true }]); + referenceSettings.forEach((f) => { + if (!this._fieldHandlerCache[f.Name]) { this._fieldHandlerCache[f.Name] = f.AllowMultiple ? new ContentListReferenceField(this[f.Name], f, this._repository) : new ContentReferenceField(this[f.Name], f, this._repository); } else { - this._fieldHandlerCache[f.Name].handleLoaded(this[f.Name]); + this._fieldHandlerCache[f.Name].HandleLoaded(this[f.Name]); } this[f.Name] = this._fieldHandlerCache[f.Name]; }); - const binarySettings: FieldSettings.BinaryFieldSetting[] = this.GetSchema().FieldSettings.filter(f => f instanceof FieldSettings.BinaryFieldSetting); + const binarySettings: FieldSettings.BinaryFieldSetting[] = this.GetSchema().FieldSettings.filter((f) => FieldSettings.isFieldSettingOfType(f, FieldSettings.BinaryFieldSetting)); - binarySettings.forEach(s => { - if (!(this[s.Name] instanceof BinaryField)){ + binarySettings.forEach((s) => { + if (!(this[s.Name] instanceof BinaryField)) { const mediaResourceObject = this[s.Name]; - this[s.Name] = new BinaryField(mediaResourceObject, this as SavedContent, s); + this[s.Name] = new BinaryField(mediaResourceObject, this as SavedContent, s); } }); + } + private tryGetAsSaved() { + if (isSavedContent(this)) { + return this as SavedContent; + } + throw new Error('Contnet is not saved.'); } /** @@ -262,9 +232,9 @@ export class Content { * @param {IContentOptions} options An object implementing IContentOptions interface * @param {IRepository} repository The Repoitory instance */ - constructor(public readonly options: T, private _repository: BaseRepository) { - Object.assign(this, options); - Object.assign(this._lastSavedFields, options); + constructor(_options: T, private _repository: BaseRepository, private readonly _contentType: {new(...args): T}) { + Object.assign(this, _options); + Object.assign(this._lastSavedFields, _options); this.updateReferenceFields(); } @@ -282,8 +252,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Delete(permanently?: boolean): Observable { + */ + public Delete(permanently?: boolean): Observable { if (this.Id) { this._isOperationInProgress = true; const fields = this.GetFields(); @@ -296,12 +266,12 @@ export class Content { this._isOperationInProgress = false; }, (err) => { this._repository.Events.Trigger.ContentDeleteFailed({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), Permanently: permanently || false, Error: err }); this._isOperationInProgress = false; - }) + }); return observable; } return Observable.of(undefined); @@ -322,13 +292,13 @@ export class Content { * }); * ``` */ - Rename(newDisplayName: string, newName?: string): Observable> { + public Rename(newDisplayName: string, newName?: string): Observable> { this._isOperationInProgress = true; if (!this.IsSaved) { - throw new Error('Content is not saved. You can rename only saved content!') + throw new Error('Content is not saved. You can rename only saved content!'); } - let fields: T = {} as T; + const fields: T = {} as T; if (newDisplayName) { fields.DisplayName = newDisplayName; } @@ -338,15 +308,14 @@ export class Content { return this.Save(fields); } - private saveContentInternal(fields?: T, override?: boolean): Observable> { - const contentType = this.constructor as { new(...args: any[]): any }; + private saveContentInternal(fields?: T, override?: boolean): Observable> { const originalFields = this.GetFields(); /** Fields Save logic */ if (fields) { if (!this.Id) { const err = new Error('Content Id not present'); this._repository.Events.Trigger.ContentModificationFailed({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), Fields: fields, Error: err }); @@ -354,41 +323,40 @@ export class Content { } if (!this.IsSaved) { - const err = new Error('The Content is not saved to the Repository, Save it before updating.') + const err = new Error('The Content is not saved to the Repository, Save it before updating.'); this._repository.Events.Trigger.ContentModificationFailed({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), Fields: fields, Error: err }); throw err; } if (override) { - const request = this._odata.Put(this.Id, contentType, fields) - .map(newFields => { + const request = this._odata.Put(this.Id, fields) + .map((newFields) => { this.updateLastSavedFields(newFields); this._repository.Events.Trigger.ContentModified({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), OriginalFields: originalFields, Changes: fields }); - return this as SavedContent; + return this.tryGetAsSaved(); }).share(); - request.subscribe(() => { }, err => { - this._repository.Events.Trigger.ContentModificationFailed({ Content: this as SavedContent, Fields: fields, Error: err }); - }) + request.subscribe(() => { /**/ }, (err) => { + this._repository.Events.Trigger.ContentModificationFailed({ Content: this.tryGetAsSaved(), Fields: fields, Error: err }); + }); return request; - } - else { - const request = this._odata.Patch(this.Id, contentType, fields) - .map(newFields => { + } else { + const request = this._odata.Patch(this.Id, fields) + .map((newFields) => { this.updateLastSavedFields(newFields); - this._repository.Events.Trigger.ContentModified({ Content: this as SavedContent, OriginalFields: originalFields, Changes: fields }); - return this as SavedContent; + this._repository.Events.Trigger.ContentModified({ Content: this.tryGetAsSaved(), OriginalFields: originalFields, Changes: fields }); + return this.tryGetAsSaved(); }).share(); - request.subscribe(() => { }, err => { - this._repository.Events.Trigger.ContentModificationFailed({ Content: this as SavedContent, Fields: fields, Error: err }); - }) + request.subscribe(() => { /**/ }, (err) => { + this._repository.Events.Trigger.ContentModificationFailed({ Content: this.tryGetAsSaved(), Fields: fields, Error: err }); + }); return request; } @@ -398,25 +366,25 @@ export class Content { // Content not saved, verify Path and POST it if (!this.Path) { const err = new Error('Cannot create content without a valid Path specified'); - this._repository.Events.Trigger.ContentCreateFailed({ Content: this, Error: err }); + this._repository.Events.Trigger.ContentCreateFailed({ Content: this as IContent, Error: err }); throw err; } - const request = this._odata.Post(this.Path, this.GetFields(true), contentType) - .map(resp => { + const request = this._odata.Post(this.Path, Object.assign({Type: this.Type}, this.GetFields(true))) + .map((resp) => { if (!resp.Id) { throw Error('Error: No content Id in response!'); } this.updateLastSavedFields(resp); - this._repository['_loadedContentReferenceCache'][resp.Id] = this as SavedContent; + this._repository.HandleLoadedContent(this.tryGetAsSaved()); this._isSaved = true; - return this as SavedContent; + return this.tryGetAsSaved(); }).share(); request.subscribe((c) => { - this._repository.Events.Trigger.ContentCreated({ Content: this as SavedContent }); - }, err => { - this._repository.Events.Trigger.ContentCreateFailed({ Content: this, Error: err }); + this._repository.Events.Trigger.ContentCreated({ Content: this.tryGetAsSaved() }); + }, (err) => { + this._repository.Events.Trigger.ContentCreateFailed({ Content: this as IContent, Error: err }); }); return request; @@ -424,24 +392,24 @@ export class Content { // Content saved if (!this.IsDirty) { // No changes, no request - return Observable.of(this as SavedContent); + return Observable.of(this.tryGetAsSaved()); } else { if (!this.Id) { throw new Error('Content Id not present'); } const changes = this.GetChanges(); // Patch content - const request = this._odata.Patch(this.Id, contentType, changes) - .map(resp => { + const request = this._odata.Patch(this.Id, changes) + .map((resp) => { this.updateLastSavedFields(resp); - return this as SavedContent; + return this.tryGetAsSaved(); }).share(); request.subscribe(() => { - this._repository.Events.Trigger.ContentModified({ Content: this as SavedContent, Changes: changes, OriginalFields: originalFields }); - }, err => { - this._repository.Events.Trigger.ContentModificationFailed({ Content: this as SavedContent, Fields: changes, Error: err }); + this._repository.Events.Trigger.ContentModified({ Content: this.tryGetAsSaved(), Changes: changes, OriginalFields: originalFields }); + }, (err) => { + this._repository.Events.Trigger.ContentModificationFailed({ Content: this.tryGetAsSaved(), Fields: changes, Error: err }); - }) + }); return request; } } @@ -464,11 +432,11 @@ export class Content { * }); * ``` */ - Save(fields?: T, override?: boolean): Observable> { + public Save(fields?: T, override?: boolean): Observable> { this._isOperationInProgress = true; const saveObservable = this.saveContentInternal(fields, override).share(); - saveObservable.subscribe(success => { + saveObservable.subscribe((success) => { this._isOperationInProgress = false; }, (err) => { this._isOperationInProgress = false; @@ -476,37 +444,36 @@ export class Content { return saveObservable; } - /** * Reloads every field and reference of the content, based on the specified View from the Schema * @throws if the Content is not saved yet or no Id or Path is provided * @param {'edit' | 'view'} actionName * @returns {Observable} An observable whitch will be updated with the Content */ - Reload(actionName?: 'edit' | 'view'): Observable> { + public Reload(actionName?: 'edit' | 'view'): Observable> { if (!this.IsSaved) { - throw new Error('Content has to be saved to reload') + throw new Error('Content has to be saved to reload'); } if (!this.Id && !this.Path) { - throw new Error('Content Id or Path has to be provided') + throw new Error('Content Id or Path has to be provided'); } - let selectFields: ODataFieldParameter | 'all' = 'all'; - let expandFields: ODataFieldParameter | undefined = undefined; + let selectFields: ODataFieldParameter | 'all' = 'all'; + let expandFields: ODataFieldParameter | undefined; if (actionName) { - const fieldSettings = this.GetSchema().FieldSettings.filter(f => { + const fieldSettings = this.GetSchema().FieldSettings.filter((f) => { return actionName === 'edit' && f.VisibleEdit - || actionName === 'view' && f.VisibleBrowse + || actionName === 'view' && f.VisibleBrowse; }); - selectFields = fieldSettings.map(f => f.Name) as ODataFieldParameter; - expandFields = fieldSettings.filter(f => f instanceof FieldSettings.ReferenceFieldSetting) - .map(f => f.Name) as ODataFieldParameter; + selectFields = fieldSettings.map((f) => f.Name) as ODataFieldParameter; + expandFields = fieldSettings.filter((f) => FieldSettings.isFieldSettingOfType(f, FieldSettings.ReferenceFieldSetting)) + .map((f) => f.Name) as ODataFieldParameter; } return this._repository.Load(this.Id || this.Path as any, { select: selectFields, expand: expandFields - } as IODataParams); + } as IODataParams); } /** @@ -515,21 +482,21 @@ export class Content { * @throws if the Content is not saved yet or no Id or Path is provided * @returns {Observable} An observable whitch will be updated with the Content */ - ReloadFields(...fields: (keyof this['options'])[]): Observable { + public ReloadFields(...fields: (keyof T)[]): Observable> { if (!this.IsSaved) { - throw new Error('Content has to be saved to reload') + throw new Error('Content has to be saved to reload'); } if (!this.Id && !this.Path) { - throw new Error('Content Id or Path has to be provided') + throw new Error('Content Id or Path has to be provided'); } - const toExpand = this.GetSchema().FieldSettings.filter(f => fields.indexOf(f.Name as any) >= 0 && f instanceof FieldSettings.ReferenceFieldSetting) - .map(f => f.Name) as ODataFieldParameter; - return this._repository.Load(this.Id || this.Path as any, { + const toExpand = this.GetSchema().FieldSettings.filter((f) => fields.indexOf(f.Name as any) >= 0 && FieldSettings.isFieldSettingOfType(f, FieldSettings.ReferenceFieldSetting)) + .map((f) => f.Name) as ODataFieldParameter; + return this._repository.Load(this.Id || this.Path as any, { select: fields, expand: toExpand - } as IODataParams); + } as IODataParams); } /** @@ -547,15 +514,15 @@ export class Content { * }); * ``` */ - Actions(scenario?: string): Observable { + public Actions(scenario?: string): Observable { return this._odata.Get({ path: ODataHelper.joinPaths(this.GetFullPath(), 'Actions'), params: { - scenario: scenario + scenario } - }, Object as { new(...args) }) - .map(resp => { - return resp.d.Actions as ActionModel[]; + }) + .map((resp) => { + return (resp.d as any).Actions as ActionModel[]; }); } /** @@ -573,7 +540,7 @@ export class Content { * }); * ``` */ - GetAllowedChildTypes(options?: IODataParams): Observable[]> { + public GetAllowedChildTypes(options?: IODataParams): Observable<(ContentType & ISavedContent)[]> { return this.AllowedChildTypes.GetContent(options); } /** @@ -591,7 +558,7 @@ export class Content { * }); * ``` */ - GetEffectiveAllowedChildTypes(options?: IODataParams): Observable[]> { + public GetEffectiveAllowedChildTypes(options?: IODataParams): Observable<(ContentType & ISavedContent)[]> { return this.EffectiveAllowedChildTypes.GetContent(options); } /** @@ -609,11 +576,10 @@ export class Content { * }); * ``` */ - GetOwner(options?: IODataParams): Observable> { + public GetOwner(options?: IODataParams): Observable { return this.Owner.GetContent(options); } - /** * Method that returns creator of a content. * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. @@ -629,7 +595,7 @@ export class Content { * }); * ``` */ - Creator(options?: IODataParams): Observable> { + public Creator(options?: IODataParams): Observable { return this.CreatedBy.GetContent(options); } /** @@ -647,7 +613,7 @@ export class Content { * }); * ``` */ - Modifier(options?: IODataParams): Observable> { + public Modifier(options?: IODataParams): Observable { return this.ModifiedBy.GetContent(options); } /** @@ -665,7 +631,7 @@ export class Content { * }); * ``` */ - CheckedOutBy(options?: IODataParams): Observable> { + public CheckedOutBy(options?: IODataParams): Observable { return this.CheckedOutTo.GetContent(options); } /** @@ -687,7 +653,7 @@ export class Content { * }); * ``` */ - Children(options?: IODataParams): Observable { + public Children(options?: IODataParams): Observable { if (!this.Path) { throw new Error('No path specified'); } @@ -695,8 +661,8 @@ export class Content { return this._odata.Fetch({ path: this.Path, params: options - }, Content).map(resp => { - return resp.d.results.map(c => this._repository.HandleLoadedContent(c as any)); + }).map((resp) => { + return resp.d.results.map((c) => this._repository.HandleLoadedContent(c)); }); } /** @@ -717,8 +683,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - GetVersions(options?: IODataParams): Observable[]> { + */ + public GetVersions(options?: IODataParams): Observable[]> { return this.Versions.GetContent(options); } /** @@ -739,8 +705,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - GetWorkspace(options?: IODataParams): Observable> { + */ + public GetWorkspace(options?: IODataParams): Observable> { return this.Workspace.GetContent(options); } /** @@ -756,8 +722,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Checkout() { + */ + public Checkout() { return this._odata.CreateCustomAction({ name: 'CheckOut', id: this.Id, isAction: true }); } /** @@ -774,13 +740,13 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - CheckIn(checkInComments?: string) { + */ + public CheckIn(checkInComments?: string) { let action; (typeof checkInComments !== 'undefined') ? action = { name: 'CheckIn', id: this.Id, isAction: true, params: ['checkInComments'] } : action = { name: 'CheckIn', id: this.Id, isAction: true }; - return this._odata.CreateCustomAction(action, { data: { 'checkInComments': checkInComments ? checkInComments : '' } }); + return this._odata.CreateCustomAction(action, { data: { checkInComments: checkInComments ? checkInComments : '' } }); } /** * Performs an undo check out operation on a content item in the Content Repository. @@ -795,8 +761,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - UndoCheckout() { + */ + public UndoCheckout() { return this._odata.CreateCustomAction({ name: 'UndoCheckOut', id: this.Id, isAction: true }); } /** @@ -812,8 +778,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - ForceUndoCheckout() { + */ + public ForceUndoCheckout() { return this._odata.CreateCustomAction({ name: 'ForceUndoCheckout', id: this.Id, isAction: true }); } /** @@ -830,8 +796,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Approve() { + */ + public Approve() { return this._odata.CreateCustomAction({ name: 'Approve', id: this.Id, isAction: true }); } /** @@ -849,10 +815,10 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Reject(rejectReason?: string) { + */ + public Reject(rejectReason?: string) { return this._odata.CreateCustomAction({ name: 'Reject', id: this.Id, isAction: true, params: ['rejectReason'] }, - { data: { 'rejectReason': rejectReason ? rejectReason : '' } }); + { data: { rejectReason: rejectReason ? rejectReason : '' } }); } /** * Performs a publish operation on a content, the equivalent of calling Publish() on the Content instance in .NET. Also checks whether the content handler of the subject content @@ -868,8 +834,8 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Publish() { + */ + public Publish() { return this._odata.CreateCustomAction({ name: 'Publish', id: this.Id, isAction: true }); } /** @@ -887,9 +853,9 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - RestoreVersion(version: string) { - return this._odata.CreateCustomAction({ name: 'Publish', id: this.Id, isAction: true, requiredParams: ['version'] }, { data: { 'version': version ? version : '' } }); + */ + public RestoreVersion(version: string) { + return this._odata.CreateCustomAction({ name: 'Publish', id: this.Id, isAction: true, requiredParams: ['version'] }, { data: { version: version ? version : '' } }); } /** * Restores a deleted content from the Trash. You can call this action only on a TrashBag content that contains the deleted content itself. @@ -906,13 +872,13 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - Restore(destination?: string, newname?: boolean) { + */ + public Restore(destination?: string, newname?: boolean) { return this._odata.CreateCustomAction( { name: 'Restore', id: this.Id, isAction: true, params: ['destination', 'newname'] }, { data: { - 'destination': destination ? destination : '', - 'newname': newname ? newname : '' + destination: destination ? destination : '', + newname: newname ? newname : '' } }); } @@ -930,11 +896,11 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - MoveTo(toPath: string) { + */ + public MoveTo(toPath: string) { if (!this.IsSaved) { - throw new Error('Content not saved!') + throw new Error('Content not saved!'); } if (!this.Path) { @@ -946,26 +912,26 @@ export class Content { } if (toPath.indexOf(this.Path) === 0) { - throw new Error('Content cannot be moved below itself') + throw new Error('Content cannot be moved below itself'); } const request = this._odata.CreateCustomAction({ name: 'MoveTo', id: this.Id, isAction: true, requiredParams: ['targetPath'] } - , { data: { 'targetPath': toPath } }); + , { data: { targetPath: toPath } }); const fromPath = this.Path; const newPath = ODataHelper.joinPaths(toPath, this.Name); - request.subscribe(result => { + request.subscribe((result) => { this.Path = newPath; this.updateLastSavedFields({ Path: newPath } as T); this._repository.Events.Trigger.ContentMoved({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), From: fromPath, To: toPath - }) - }, err => { + }); + }, (err) => { this._repository.Events.Trigger.ContentMoveFailed({ - Content: this as SavedContent, + Content: this.tryGetAsSaved(), From: fromPath, To: toPath, Error: err @@ -987,10 +953,10 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - CopyTo(path: string) { + */ + public CopyTo(path: string) { return this._odata.CreateCustomAction({ name: 'CopyTo', id: this.Id, isAction: true, requiredParams: ['targetPath'] }, - { data: { 'targetPath': path } }); + { data: { targetPath: path } }); } /** * Adds the given content types to the Allowed content Type list. @@ -1006,9 +972,9 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - AddAllowedChildTypes(contentTypes: string[]) { - return this._odata.CreateCustomAction({ name: 'AddAllowedChildTypes', id: this.Id, isAction: true, requiredParams: ['contentTypes'] }, { data: { 'contentTypes': contentTypes } }); + */ + public AddAllowedChildTypes(contentTypes: string[]) { + return this._odata.CreateCustomAction({ name: 'AddAllowedChildTypes', id: this.Id, isAction: true, requiredParams: ['contentTypes'] }, { data: { contentTypes } }); } /** * Removes the given content types from the Allowed content Type list. If the list after removing and the list on the matching CTD are the same, the local list will be removed. @@ -1024,50 +990,21 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - RemoveAllowedChildTypes(contentTypes: string[]) { - return this._odata.CreateCustomAction({ name: 'RemoveAllowedChildTypes', id: this.Id, isAction: true, requiredParams: ['contentTypes'] }, - { data: { 'contentTypes': contentTypes } }); - } - - private static _schemaCache: Schemas.Schema[] = []; - /** - * Returns the Content Type Schema of the given Content Type; - * @param type {string} The name of the Content Type; - * @returns {Schemas.Schema} - * ```ts - * var genericContentSchema = SenseNet.Content.getSchema(Content); - * ``` */ - public static GetSchema(currentType: { new(...args: any[]): TType }): Schemas.Schema { - if (this._schemaCache[currentType.name as any]) { - return this._schemaCache[currentType.name as any] as Schemas.Schema; - } - const schema = Schemas.SchemaStore.find(s => s.ContentType === currentType) as Schemas.Schema; - if (!schema) { - return Content.GetSchema(GenericContent as any) as Schemas.Schema; - } - const parent = Object.getPrototypeOf(currentType); - const parentSchema = parent && Schemas.SchemaStore.find(s => s.ContentType === parent); - - if (parentSchema) { - let parentSchema = Content.GetSchema(parent); - schema.FieldSettings = schema.FieldSettings.concat(parentSchema.FieldSettings); - } - this._schemaCache[currentType.name as any] = schema; - return schema; + public RemoveAllowedChildTypes(contentTypes: string[]) { + return this._odata.CreateCustomAction({ name: 'RemoveAllowedChildTypes', id: this.Id, isAction: true, requiredParams: ['contentTypes'] }, + { data: { contentTypes } }); } /** * Returns the Content Type Schema of the Content. * @returns {Schemas.Schema} Array of fieldsettings. - *```ts + * ```ts * let schema = SenseNet.Content.GetSchema(Content); - *``` + * ``` */ - GetSchema(): Schemas.Schema { - const contentType = (ContentTypes as any)[this.Type] as { new(...args) }; - return Content.GetSchema(contentType || this.constructor as { new(...args: any[]): any }); + public GetSchema(): Schemas.Schema { + return this._repository.GetSchema(this._contentType); } /** @@ -1079,20 +1016,18 @@ export class Content { * var content = SenseNet.Content.Create({ DisplayName: 'My folder' }, ContentTypes.Folder); // content is an instance of the ContentTypes.Folder with the DisplayName 'My folder' * ``` */ - public static Create(opt: O, newContent: { new(...args: any[]): TContent }, - repository: BaseRepository): TContent { - let constructed = new newContent(opt, repository); - return constructed; - } - + // public static Create(opt: O, newContent: { new(...args: any[]): TContent }, + // repository: BaseRepository): TContent { + // let constructed = new newContent(opt, repository); + // return constructed; + // } - // Shortcut to repository.HandleLoadedContent() - // ToDo: Remove. Deprecated since ~2.1.0 - 2017.07.14. - public static HandleLoadedContent: (contentType: { new(...args: any[]): TContent }, opt: O & { Id: number, Path: string }, - repository: BaseRepository) => SavedContent - = (contentType, contentOptions, repository) => { - console.warn('Method Content.HandleLoadedContent is deprecated and will be removed in the upcoming release. Please use repository.HandleLoadedContent instead.') - return repository.HandleLoadedContent(contentOptions, contentType); + public static Create(options: T, newContent: {new(...args: any[]): T}, repository: BaseRepository): Content & T { + const created = new ContentInternal(options, repository, newContent) as Content; + if (newContent) { + created.Type = newContent.name; + } + return created; } /** @@ -1114,53 +1049,52 @@ export class Content { * complete: () => console.log('done'), * }); * ``` - */ - - /** - * Sets permissions on the requested content. You can add or remove permissions for one ore more users or groups using this action or even break/unbreak permission inheritance. - * @param inheritance {Security.Inheritance} inheritance: break or unbreak - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let setPermissions = content.SetPermissions({inheritance:"break"}); - * setPermissions.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - SetPermissions(arg: Security.Inheritance | Security.PermissionRequestBody[]) { + */ + + /** + * Sets permissions on the requested content. You can add or remove permissions for one ore more users or groups using this action or even break/unbreak permission inheritance. + * @param inheritance {Security.Inheritance} inheritance: break or unbreak + * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * ``` + * let setPermissions = content.SetPermissions({inheritance:"break"}); + * setPermissions.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public SetPermissions(arg: Security.Inheritance | Security.PermissionRequestBody[]) { if (arg instanceof Array) { return this._odata.CreateCustomAction({ name: 'SetPermissions', id: this.Id, isAction: true, requiredParams: ['entryList'] }, - { data: { 'entryList': arg } }); - } - else { + { data: { entryList: arg } }); + } else { return this._odata.CreateCustomAction({ name: 'SetPermissions', path: this.Path, isAction: true, requiredParams: ['inheritance'] }, - { data: { 'inheritance': arg } }); + { data: { inheritance: arg } }); } - }; + } /** * Gets permissions for the requested content. If no identity is given, all the permission entries will be returned. * * Required permissions to call this action: See permissions. * @params identity {string=} path of the identity whose permissions must be returned (user, group or organizational unit) * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getPermissions = content.GetPermission('/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members'); - * getPermissions.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetPermission(identity?: string) { + * ``` + * let getPermissions = content.GetPermission('/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members'); + * getPermissions.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetPermission(identity?: string) { return this._odata.CreateCustomAction({ name: 'GetPermission', id: this.Id, isAction: false, params: ['identity'] }, - { data: { 'identity': identity ? identity : '' } }); + { data: { identity: identity ? identity : '' } }); } /** * Gets if the given user (or if it is not given than the current user) has the specified permissions for the requested content. @@ -1169,51 +1103,52 @@ export class Content { * @params permissions {string[]} list of permission names (e.g. Open, Save) * @params user {string} [CurrentUser] path of the user * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let hasPermission = content.HasPermission(['AddNew', 'Save']); - * hasPermission.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - HasPermission(permissions: ('See' | 'Preview' | 'PreviewWithoutWatermark' | 'PreviewWithoutRedaction' | 'Open' | + * ``` + * let hasPermission = content.HasPermission(['AddNew', 'Save']); + * hasPermission.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public HasPermission(permissions: ('See' | 'Preview' | 'PreviewWithoutWatermark' | 'PreviewWithoutRedaction' | 'Open' | 'OpenMinor' | 'Save' | 'Publish' | 'ForceCheckin' | 'AddNew' | 'Approve' | 'Delete' | 'RecallOldVersion' | 'DeleteOldVersion' | 'SeePermissions' | 'SetPermissions' | 'RunApplication' | 'ManageListsAndWorkspaces' | 'TakeOwnership' | 'Custom01' | 'Custom02' | 'Custom03' | 'Custom04' | 'Custom05' | 'Custom06' | 'Custom07' | 'Custom08' | 'Custom09' | 'Custom10' | 'Custom11' | 'Custom12' | 'Custom13' | 'Custom14' | 'Custom15' | 'Custom16' | 'Custom17' | 'Custom18' | 'Custom19' | 'Custom20' | 'Custom21' | 'Custom22' | 'Custom23' | 'Custom24' | 'Custom25' | - 'Custom26' | 'Custom27' | 'Custom28' | 'Custom29' | 'Custom30' | 'Custom31' | 'Custom32')[], identity?: User | Group): Observable { + 'Custom26' | 'Custom27' | 'Custom28' | 'Custom29' | 'Custom30' | 'Custom31' | 'Custom32')[], + identity?: User | Group): Observable { let params = `permissions=${permissions.join(',')}`; if (identity && identity.Path) { - params += `&identity=${identity.Path}` - }; - return this._repository.Ajax(`${this.GetFullPath()}/HasPermission?${params}`, 'GET', Boolean as { new() }); + params += `&identity=${identity.Path}`; + } + return this._repository.Ajax(`${this.GetFullPath()}/HasPermission?${params}`, 'GET'); } /** * Users who have TakeOwnership permission for the current content can modify the Owner of this content. * @params userOrGroup {string} path or the id of the new owner (that can be a Group or a User). The input parameter also supports empty or null string, * in this case the new owner will be the current user. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let takeOwnerShip = content.TakeOwnership({'userGroup':'/Root/IMS/BuiltIn/Portal/Admin'}); - * takeOwnerShip.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - TakeOwnership(userOrGroup?: string) { + * ``` + * let takeOwnerShip = content.TakeOwnership({'userGroup':'/Root/IMS/BuiltIn/Portal/Admin'}); + * takeOwnerShip.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public TakeOwnership(userOrGroup?: string) { return this._odata.CreateCustomAction({ name: 'TakeOwnership', id: this.Id, isAction: true, params: ['userOrGroup'] }, - { data: { 'userOrGroup': userOrGroup ? userOrGroup : '' } }); + { data: { userOrGroup: userOrGroup ? userOrGroup : '' } }); } /** * Creates or modifies a {Query} content. Use this action instead of creating query content directly using the basic OData create method, because query content can be saved @@ -1222,24 +1157,24 @@ export class Content { * @params displayName {string} Desired display name for the query content. Can be empty. * @params queryType {ComplexTypes.SavedQueryType} [Public] Type of the saved query. If an empty value is posted, the default is Public. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let saveQuery = content.SaveQuery({ - * 'query':'DisplayName:Africa', - * 'displayName': 'My query', - * 'queryType': 'Private' - * }); - * saveQuery.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - SaveQuery(query: string, displayName: string, queryType: Enums.QueryType) { + * ``` + * let saveQuery = content.SaveQuery({ + * 'query':'DisplayName:Africa', + * 'displayName': 'My query', + * 'queryType': 'Private' + * }); + * saveQuery.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public SaveQuery(query: string, displayName: string, queryType: Enums.QueryType) { return this._odata.CreateCustomAction({ name: 'SaveQuery', id: this.Id, isAction: true, requiredParams: ['query', 'displayName', 'queryType'] }, - { data: { 'query': query, 'displayName': displayName ? displayName : '', queryType: queryType } }); + { data: { query, displayName: displayName ? displayName : '', queryType } }); } /** * Gets Query content that are relevant in the current context. The result set will contain two types of content: @@ -1247,163 +1182,163 @@ export class Content { * * Private queries: query content in the Queries content list under the profile of the current user * @params onlyPublic {boolean} if true, only public queries are returned from the current workspace. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getQueries = content.GetQueries(true); - * getQueries.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetQueries(onlyPublic: boolean = true) { + * ``` + * let getQueries = content.GetQueries(true); + * getQueries.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetQueries(onlyPublic: boolean = true) { return this._odata.CreateCustomAction({ name: 'GetQueries', id: this.Id, isAction: false, noCache: true, requiredParams: ['onlyPublic'] }, - { data: { 'onlyPublic': onlyPublic } }); + { data: { onlyPublic } }); } /** * Closes a Multistep saving operation and sets the saving state of a content to Finalized. Can be invoked only on content that are not already finalized. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let finalize = content.FinalizeContent(true); - * finalize.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - Finalize() { + * ``` + * let finalize = content.FinalizeContent(true); + * finalize.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public Finalize() { return this._odata.CreateCustomAction({ name: 'FinalizeContent', id: this.Id, isAction: true }); } /** - * Lets administrators take over the lock of a checked out document from another user. A new locker user can be provided using the 'user' parameter (user path or id as string). - * If left empty, the current user will take the lock. - * @params userId {number=} - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let takeLockOver = content.TakeLockOver(true); - * takeLockOver.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - TakeLockOver(userId?: number) { + * Lets administrators take over the lock of a checked out document from another user. A new locker user can be provided using the 'user' parameter (user path or id as string). + * If left empty, the current user will take the lock. + * @params userId {number=} + * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * ``` + * let takeLockOver = content.TakeLockOver(true); + * takeLockOver.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public TakeLockOver(userId?: number) { return this._odata.CreateCustomAction({ name: 'TakeLockOver', id: this.Id, isAction: true, params: ['user'] }, - { data: { 'user': userId ? userId : '' } }); + { data: { user: userId ? userId : '' } }); } /** * These actions perform an indexing operation on a single content or a whole subtree. * @params recursive {boolean=} * @params rebuildLevel {number=} * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let rebuildIndex = content.RebuildIndex(true); - * rebuildIndex.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` + * ``` + * let rebuildIndex = content.RebuildIndex(true); + * rebuildIndex.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` */ - RebuildIndex(recursive?: boolean, rebuildLevel?: number) { - return this._odata.CreateCustomAction({ name: 'RebuildIndex', id: this.Id, isAction: true, params: ['recursive', 'rebuildLevel'] }, { data: { 'recursive': recursive ? recursive : false, 'rebuildLevel': rebuildLevel ? rebuildLevel : 0 } }); + public RebuildIndex(recursive?: boolean, rebuildLevel?: number) { + return this._odata.CreateCustomAction({ name: 'RebuildIndex', id: this.Id, isAction: true, params: ['recursive', 'rebuildLevel'] }, { data: { recursive: recursive ? recursive : false, rebuildLevel: rebuildLevel ? rebuildLevel : 0 } }); } /** * Performs a full reindex operation on the content and the whole subtree. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let rebuildIndexSubtree = content.RebuildIndexSubtree(); - * rebuildIndexSubtree.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - RebuildIndexSubtree() { + * ``` + * let rebuildIndexSubtree = content.RebuildIndexSubtree(); + * rebuildIndexSubtree.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public RebuildIndexSubtree() { return this._odata.CreateCustomAction({ name: 'RebuildIndexSubtree', id: this.Id, isAction: true }); } /** * Refreshes the index document of the content and the whole subtree using the already existing index data stored in the database. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let refreshIndexSubtree = content.RefreshIndexSubtree(); - * refreshIndexSubtree.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - RefreshIndexSubtree() { + * ``` + * let refreshIndexSubtree = content.RefreshIndexSubtree(); + * refreshIndexSubtree.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public RefreshIndexSubtree() { return this._odata.CreateCustomAction({ name: 'RefreshIndexSubtree', id: this.Id, isAction: true }); } /** * Returns the number of currently existing preview images. If necessary, it can make sure that all preview images are generated and available for a document. * @ params generateMissing {boolean=} * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let checkPreviews = content.CheckPreviews(true); - * checkPreviews.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - CheckPreviews(generateMissing?: boolean) { + * ``` + * let checkPreviews = content.CheckPreviews(true); + * checkPreviews.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public CheckPreviews(generateMissing?: boolean) { return this._odata.CreateCustomAction({ name: 'CheckPreviews', id: this.Id, isAction: true, params: ['generateMissing'] }, - { data: { 'generateMissing': generateMissing ? generateMissing : false } }); + { data: { generateMissing: generateMissing ? generateMissing : false } }); } /** * It clears all existing preview images for a document and starts a task for generating new ones. This can be useful in case the preview status of a document has been set to 'error' * before for some reason and you need to force the system to re-generate preview images. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let regeneratePreviews = content.RegeneratePreviews(); - * regeneratePreviews.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - RegeneratePreviews() { + * ``` + * let regeneratePreviews = content.RegeneratePreviews(); + * regeneratePreviews.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public RegeneratePreviews() { return this._odata.CreateCustomAction({ name: 'RegeneratePreviews', id: this.Id, isAction: true }); } /** * Returns the number of pages in a document. If there is no information about page count on the content, it starts a preview generation task to determine the page count. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getPageCount = content.GetPageCount(); - * getPageCount.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetPageCount() { + * ``` + * let getPageCount = content.GetPageCount(); + * getPageCount.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetPageCount() { return this._odata.CreateCustomAction({ name: 'GetPageCount', id: this.Id, isAction: true }); } /** @@ -1412,70 +1347,73 @@ export class Content { * first. It is OK to call this method periodically for checking if an image is already available. * @params page {number} * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let previewAvailable = content.PreviewAvailable(2); - * previewAvailable.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - PreviewAvailable(page: number) { + * ``` + * let previewAvailable = content.PreviewAvailable(2); + * previewAvailable.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public PreviewAvailable(page: number) { return this._odata.CreateCustomAction({ name: 'PreviewAvailable', id: this.Id, isAction: false, requiredParams: ['page'] }, - { data: { 'page': page } }); + { data: { page } }); } /** * Returns the full list of preview images as content items. This method synchronously generates all missing preview images. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getPreviewImagesForOData = content.GetPreviewImagesForOData(); - * getPreviewImagesForOData.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetPreviewImagesForOData() { + * ``` + * let getPreviewImagesForOData = content.GetPreviewImagesForOData(); + * getPreviewImagesForOData.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + // tslint:disable-next-line:naming-convention + public GetPreviewImagesForOData() { return this._odata.CreateCustomAction({ name: 'GetPreviewImagesForOData', id: this.Id, isAction: false }); } /** * Returns the list of existing preview images (only the first consecutive batch) as objects with a few information (image path, dimensions). It does not generate any new images. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getExistingPreviewImagesForOData = content.GetExistingPreviewImagesForOData(); - * getExistingPreviewImagesForOData.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetExistingPreviewImagesForOData() { + * ``` + * let getExistingPreviewImagesForOData = content.GetExistingPreviewImagesForOData(); + * getExistingPreviewImagesForOData.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + // tslint:disable-next-line:naming-convention + public GetExistingPreviewImagesForOData() { return this._odata.CreateCustomAction({ name: 'GetExistingPreviewImagesForOData', id: this.Id, isAction: false }); } /** * Returns the list of the AllowedChildTypes which are set on the current Content. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getAllowedChildTypesFromCTD = content.GetAllowedChildTypesFromCTD(); - * getAllowedChildTypesFromCTD.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetAllowedChildTypesFromCTD() { + * ``` + * let getAllowedChildTypesFromCTD = content.GetAllowedChildTypesFromCTD(); + * getAllowedChildTypesFromCTD.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + // tslint:disable-next-line:naming-convention + public GetAllowedChildTypesFromCTD() { return this._odata.CreateCustomAction({ name: 'GetAllowedChildTypesFromCTD', id: this.Id, isAction: false }); } /** @@ -1483,20 +1421,20 @@ export class Content { * @params level {Security.PermissionLevel} The value is "AllowedOrDenied". "Allowed" or "Denied" are not implemented yet. * @params kind {Security.IdentityKind} The value can be: All, Users, Groups, OrganizationalUnits, UsersAndGroups, UsersAndOrganizationalUnits, GroupsAndOrganizationalUnits * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getRelatedIdentities = content.GetRelatedIdentities("AllowedOrDenied", "Groups"); - * getRelatedIdentities.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetRelatedIdentities(level: Security.PermissionLevel, kind: Security.IdentityKind) { + * ``` + * let getRelatedIdentities = content.GetRelatedIdentities("AllowedOrDenied", "Groups"); + * getRelatedIdentities.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetRelatedIdentities(level: Security.PermissionLevel, kind: Security.IdentityKind) { return this._odata.CreateCustomAction({ name: 'GetRelatedIdentities', id: this.Id, isAction: true, requiredParams: ['level', 'kind'] }, - { data: { 'level': level, 'kind': kind } }); + { data: { level, kind } }); } /** * Permission list of the selected identity with the count of related content. 0 indicates that this permission has no related content so the GUI does not have to display it as a tree node @@ -1506,20 +1444,20 @@ export class Content { * @params includedTypes {string[]} An item can increment the counters if its type or any ancestor type is found in the 'includedTypes'. Null means filtering off. If the array is empty, there * is no element that increases the counters. This filter can reduce the execution speed dramatically so do not use if it is possible. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getRelatedPermissions = content.GetRelatedPermissions("AllowedOrDenied", true, "/Root/IMS/BuiltIn/Portal/EveryOne", null); - * getRelatedPermissions.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetRelatedPermissions(level: Security.PermissionLevel, explicitOnly: boolean, member: string, includedTypes?: string[]) { + * ``` + * let getRelatedPermissions = content.GetRelatedPermissions("AllowedOrDenied", true, "/Root/IMS/BuiltIn/Portal/EveryOne", null); + * getRelatedPermissions.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetRelatedPermissions(level: Security.PermissionLevel, explicitOnly: boolean, member: string, includedTypes?: string[]) { return this._odata.CreateCustomAction({ name: 'GetRelatedPermissions', id: this.Id, isAction: true, requiredParams: ['level', 'explicitOnly', 'member', 'includedTypes'] }, - { data: { 'level': level, 'explicitOnly': explicitOnly, 'member': member, 'includedTypes': includedTypes } }); + { data: { level, explicitOnly, member, includedTypes } }); } /** * Content list that have explicite/effective permission setting for the selected user in the current subtree. @@ -1529,20 +1467,20 @@ export class Content { * @params permissions {string[]} related permission list. Item names are case sensitive. In most cases only one item is used (e.g. "See" or "Save" etc.) but you can pass any permission * type name (e.g. ["Open","Save","Custom02"]). * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getRelatedItems = content.GetRelatedItems("AllowedOrDenied", true, "/Root/IMS/BuiltIn/Portal/EveryOne", ["RunApplication"]); - * getRelatedItems.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetRelatedItems(level: Security.PermissionLevel, explicitOnly: boolean, member: string, permissions: string[]) { + * ``` + * let getRelatedItems = content.GetRelatedItems("AllowedOrDenied", true, "/Root/IMS/BuiltIn/Portal/EveryOne", ["RunApplication"]); + * getRelatedItems.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetRelatedItems(level: Security.PermissionLevel, explicitOnly: boolean, member: string, permissions: string[]) { return this._odata.CreateCustomAction({ name: 'GetRelatedItems', id: this.Id, isAction: true, requiredParams: ['level', 'explicitOnly', 'member', 'permissions'] }, - { data: { 'level': level, 'explicitOnly': explicitOnly, 'member': member, 'permissions': permissions } }); + { data: { level, explicitOnly, member, permissions } }); } /** * This structure is designed for getting tree of content that are permitted or denied for groups/organizational units in the selected subtree. The result content are not in a paged list: @@ -1552,20 +1490,20 @@ export class Content { * @params permissions {string[]} related permission list. Item names are case sensitive. In most cases only one item is used (e.g. "See" or "Save" etc.) but you can pass any permission * type name (e.g. ["Open","Save","Custom02"]). * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getRelatedIdentitiesByPermissions = content.GetRelatedIdentitiesByPermissions("AllowedOrDenied", "Groups", ["RunApplication"]); - * getRelatedIdentitiesByPermissions.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetRelatedIdentitiesByPermissions(level: Security.PermissionLevel, kind: Security.IdentityKind, permissions: string[]) { + * ``` + * let getRelatedIdentitiesByPermissions = content.GetRelatedIdentitiesByPermissions("AllowedOrDenied", "Groups", ["RunApplication"]); + * getRelatedIdentitiesByPermissions.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetRelatedIdentitiesByPermissions(level: Security.PermissionLevel, kind: Security.IdentityKind, permissions: string[]) { return this._odata.CreateCustomAction({ name: 'GetRelatedIdentitiesByPermissions', id: this.Id, isAction: true, requiredParams: ['level', 'kind', 'permissions'] }, - { data: { 'level': level, 'kind': kind, 'permissions': permissions } }); + { data: { level, kind, permissions } }); } /** * This structure is designed for getting tree of content that are permitted or denied for groups/organizational units in the selected subtree. The result content are not in a paged list: @@ -1575,20 +1513,20 @@ export class Content { * @params permissions {string[]} related permission list. Item names are case sensitive. In most cases only one item is used (e.g. "See" or "Save" etc.) but you can pass any permission * type name (e.g. ["Open","Save","Custom02"]). * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getRelatedItemsOneLevel = content.GetRelatedItemsOneLevel("AllowedOrDenied", "/Root/IMS/BuiltIn/Portal/Visitor", ["Open", "RunApplication"]); - * getRelatedItemsOneLevel.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetRelatedItemsOneLevel(level: Security.PermissionLevel, member: string, permissions: string[]) { + * ``` + * let getRelatedItemsOneLevel = content.GetRelatedItemsOneLevel("AllowedOrDenied", "/Root/IMS/BuiltIn/Portal/Visitor", ["Open", "RunApplication"]); + * getRelatedItemsOneLevel.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetRelatedItemsOneLevel(level: Security.PermissionLevel, member: string, permissions: string[]) { return this._odata.CreateCustomAction({ name: 'GetRelatedItemsOneLevel', id: this.Id, isAction: true, requiredParams: ['level', 'member', 'permissions'] }, - { data: { 'level': level, 'member': member, 'permissions': permissions } }); + { data: { level, member, permissions } }); } /** * Returns a content collection that represents users who have enough permissions to a requested resource. The permissions effect on the user and through direct or indirect group membership @@ -1596,78 +1534,78 @@ export class Content { * @params permissions {string[]} related permission list. Item names are case sensitive. In most cases only one item is used (e.g. "See" or "Save" etc.) but you can pass any permission * type name (e.g. ["Open","Save","Custom02"]). * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getAllowedUsers = content.GetAllowedUsers(["Open", "RunApplication"]); - * getAllowedUsers.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetAllowedUsers(permissions: string[]) { + * ``` + * let getAllowedUsers = content.GetAllowedUsers(["Open", "RunApplication"]); + * getAllowedUsers.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetAllowedUsers(permissions: string[]) { return this._odata.CreateCustomAction({ name: 'GetAllowedUsers', id: this.Id, isAction: true, requiredParams: ['permissions'] }, - { data: { 'permissions': permissions } }); + { data: { permissions } }); } /** * Returns a content collection that represents groups where the given user or group is member directly or indirectly. This function can be used only on a resource content that is * Group or User or any inherited type. If the value of the "directOnly" parameter is false, all indirect members are listed. * @params directOnly {boolean} If the value of the "directOnly" parameter is false, all indirect members are listed. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let getParentGroups = content.GetParentGroups(["Open", "RunApplication"]); - * getParentGroups.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - GetParentGroups(directOnly: boolean) { + * ``` + * let getParentGroups = content.GetParentGroups(["Open", "RunApplication"]); + * getParentGroups.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public GetParentGroups(directOnly: boolean) { return this._odata.CreateCustomAction({ name: 'GetParentGroups', id: this.Id, isAction: true, requiredParams: ['directOnly'] }, - { data: { 'directOnly': directOnly } }); + { data: { directOnly } }); } /** * Administrators can add new members to a group using this action. The list of new members can be provided using the 'contentIds' parameter (list of user or group ids). * @params contentIds {number[]} List of the member ids. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let addMembers = content.AddMembers([ 123, 456, 789 ]); - * addMembers.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - AddMembers(contentIds: number[]) { + * ``` + * let addMembers = content.AddMembers([ 123, 456, 789 ]); + * addMembers.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public AddMembers(contentIds: number[]) { return this._odata.CreateCustomAction({ name: 'AddMembers', id: this.Id, isAction: true, requiredParams: ['contentIds'] }, - { data: { 'contentIds': contentIds } }); + { data: { contentIds } }); } /** * Administrators can remove members from a group using this action. The list of removable members can be provided using the 'contentIds' parameter (list of user or group ids). * @params contentIds {number[]} List of the member ids. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - * ``` - * let removeMembers = content.RemoveMembers([ 123, 456, 789 ]); - * removeMembers.subscribe({ - * next: response => { - * console.log('success'); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); - * ``` - */ - RemoveMembers(contentIds: number[]) { + * ``` + * let removeMembers = content.RemoveMembers([ 123, 456, 789 ]); + * removeMembers.subscribe({ + * next: response => { + * console.log('success'); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), + * complete: () => console.log('done'), + * }); + * ``` + */ + public RemoveMembers(contentIds: number[]) { return this._odata.CreateCustomAction({ name: 'RemoveMembers', id: this.Id, isAction: true, requiredParams: ['contentIds'] }, - { data: { 'contentIds': contentIds } }); + { data: { contentIds } }); } /** @@ -1686,7 +1624,8 @@ export class Content { * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. */ public Upload(contentType: string = 'File', fileName: string, overwrite: boolean = true, useChunk: boolean = false, propertyName: string = 'Binary', fileText?: string) { - console.warn(`This method is deprecated, please use 'UploadFile' or 'UploadText' instead `) + // tslint:disable-next-line:no-console + console.warn(`This method is deprecated, please use 'UploadFile' or 'UploadText' instead `); if (!this.Path) { throw Error('No Path provided!'); } @@ -1696,36 +1635,34 @@ export class Content { FileName: fileName, Overwrite: overwrite, UseChunk: useChunk, - propertyName: propertyName, + propertyName, }; if (typeof fileText !== 'undefined') { - data['FileText'] = fileText; + data.FileText = fileText; } - let uploadCreation = this._odata.Upload(this.Path, data, true); + const uploadCreation = this._odata.Upload(this.Path, data, true); uploadCreation.subscribe({ next: (response) => { - const data = { + return this._odata.Upload(this.Path as string, { ContentType: contentType, FileName: fileName, Overwrite: overwrite, ChunkToken: response - }; - return this._odata.Upload(this.Path as any, data, false); + }, false); } }); return uploadCreation; } - /** * Uploads a File into a level below the specified Content * @param {UploadFileOptions} uploadOptions The options to the Upload request */ - public UploadFile(uploadOptions: UploadFileOptions): Observable>{ + public UploadFile(uploadOptions: UploadFileOptions): Observable> { return this._repository.UploadFile({ ...uploadOptions, - Parent: (this as SavedContent) + Parent: (this.tryGetAsSaved()) }); } @@ -1733,10 +1670,11 @@ export class Content { * Creates and uploads a text file from a string value into a level below the specified Content * @param {UploadTextOptions} uploadOptions The options to the Upload request */ - public UploadText(uploadOptions: UploadTextOptions): Observable>{ - return this._repository.UploadTextAsFile({ + public UploadText(uploadOptions: UploadTextOptions): Observable> { + const Parent = this.tryGetAsSaved(); + return this._repository.UploadTextAsFile({ ...uploadOptions, - Parent: this + Parent }); } @@ -1744,10 +1682,11 @@ export class Content { * Uploads multiple files / folders from a single Drop event into a level below a specified content * @param {UploadFromEventOptions} uploadOptions The options to the Upload request */ - public UploadFromDropEvent(uploadOptions: UploadFromEventOptions){ + public UploadFromDropEvent(uploadOptions: UploadFromEventOptions) { + const Parent = this.tryGetAsSaved(); return this._repository.UploadFromDropEvent({ ...uploadOptions, - Parent: this + Parent }); } @@ -1773,14 +1712,14 @@ export class Content { * Returns the parent content's Path in an Entity format * e.g. for the 'Child' content '/Root/Parent/Child' you will get '/Root/('Parent')' */ - public get ParentContentPath(): string{ + public get ParentContentPath(): string { return ODataHelper.getContentURLbyPath(this.ParentPath); } /** * Indicates if the current Content is the parent a specified Content */ - public IsParentOf(childContent: Content): boolean { + public IsParentOf(childContent: SavedContent): boolean { return this._repository === childContent.GetRepository() && this.IsSaved && (this.Id && childContent.ParentId === this.Id || childContent.ParentPath === this.Path); @@ -1789,7 +1728,7 @@ export class Content { /** * Indicates if the current Content is a child of a specified Content */ - public IsChildOf(parentContent: Content): boolean { + public IsChildOf(parentContent: SavedContent): boolean { return this._repository === parentContent.GetRepository() && parentContent.IsSaved && (parentContent.Id && this.ParentId === parentContent.Id || this.ParentPath === parentContent.Path); @@ -1798,9 +1737,9 @@ export class Content { /** * Indicates if the current Content is an ancestor of a specified Content */ - public IsAncestorOf(descendantContent: Content): boolean { + public IsAncestorOf(descendantContent: SavedContent): boolean { if (!descendantContent.Path || !this.Path) { - throw Error('No path provided') + throw Error('No path provided'); } return this._repository === descendantContent.GetRepository() && this.IsSaved && descendantContent.Path.indexOf(this.Path + '/') === 0; } @@ -1808,9 +1747,9 @@ export class Content { /** * Indicates if the current Content is a descendant of a specified Content */ - public IsDescendantOf(ancestorContent: Content): boolean { + public IsDescendantOf(ancestorContent: SavedContent): boolean { if (!ancestorContent.Path || !this.Path) { - throw Error('No path provided') + throw Error('No path provided'); } return this._repository === ancestorContent.GetRepository() && ancestorContent.IsSaved && this.Path.indexOf(ancestorContent.Path + '/') === 0; } @@ -1819,7 +1758,7 @@ export class Content { * Returns the full Path for the current content * @throws if the Content is not saved yet, or hasn't got an Id or Path defined */ - GetFullPath(): string { + public GetFullPath(): string { if (!this.IsSaved) { throw new Error('Content has to be saved to get the full Path'); } @@ -1836,7 +1775,7 @@ export class Content { * Creates a stringified value from the current Content * @returns {string} The stringified value */ - Stringify: () => string = () => ContentSerializer.Stringify(this); + public Stringify: () => string = () => ContentSerializer.Stringify(this.tryGetAsSaved()); /** * Creates a content query on a Content instance. @@ -1852,33 +1791,11 @@ export class Content { * ``` * @returns {Observable>} An observable with the Query result. */ - CreateQuery: (build: (first: QueryExpression) => QuerySegment, params?: IODataParams) => FinializedQuery + public CreateQuery: (build: (first: QueryExpression) => QuerySegment, params?: IODataParams) => FinializedQuery = (build, params) => { if (!this.Path) { - throw new Error('No Content path provided for querying') + throw new Error('No Content path provided for querying'); } return new FinializedQuery(build, this._repository, this.Path, params); - }; + } } - -/** -* Interface for classes that represent a Content. -* @interface IContentOptions -*/ - -export interface IContentOptions { - Type?: string; - Name?: string; - Id?: number; - DisplayName?: string; - Description?: string; - Icon?: string; - Index?: number; - CreationDate?: string; - ModificationDate?: string; - ParentId?: number; - IsFolder?: boolean; - Path?: string; - Versions?: ContentListReferenceField; - Workspace?: ContentReferenceField; -} \ No newline at end of file diff --git a/src/Content/IContent.ts b/src/Content/IContent.ts new file mode 100644 index 0000000..9c022a3 --- /dev/null +++ b/src/Content/IContent.ts @@ -0,0 +1,22 @@ +export interface IContent { + Type?: string; + /** + * Name of the Content + */ + Name?: string; + + /** + * An unique identifier for a content + */ + Id?: number; + DisplayName?: string; + Description?: string; + Icon?: string; + Index?: number; + CreationDate?: string; + ModificationDate?: string; + ParentId?: number; + IsFolder?: boolean; + Path?: string; + +} diff --git a/src/Content/ISavedContent.ts b/src/Content/ISavedContent.ts new file mode 100644 index 0000000..ae5a127 --- /dev/null +++ b/src/Content/ISavedContent.ts @@ -0,0 +1,7 @@ +import { IContent } from './IContent'; + +export interface ISavedContent extends IContent { + Id: number; + Path: string; + Name: string; +} diff --git a/src/Content/TypeGuards.ts b/src/Content/TypeGuards.ts new file mode 100644 index 0000000..fb22344 --- /dev/null +++ b/src/Content/TypeGuards.ts @@ -0,0 +1,40 @@ +import { DeferredObject } from '../ComplexTypes'; +import { ContentInternal } from './ContentInternal'; +import { IContent } from './IContent'; +import { SavedContent } from './Types'; + +/** + * Typeguard that determines if the specified Object is a DeferredObject + * @param fieldObject The object that needs to be checked + */ +export const isDeferred = (fieldObject: any): fieldObject is DeferredObject => { + return fieldObject && fieldObject.__deferred && fieldObject.__deferred.uri && fieldObject.__deferred.uri.length > 0 || false; +}; + +/** + * Typeguard that determines if the specified Object is an IContentOptions instance + * @param object The object that needs to be checked + */ +export const isIContent = (object: any): object is IContent => { + return object && object.Id && object.Path && object.Type && object.Type.length > 0 || false; +}; + +/** + * Typeguard that determines if the specified Object is a Content instance + * @param object The object that needs to be checked + */ +export const isContent = (object: any): object is ContentInternal => { + return object instanceof ContentInternal; +}; + +/** + * Typeguard that determines if the specified Object is an IContentOptions array + * @param {any[]} objectList The object that needs to be checked + */ +export const isIContentList = (objectList: any[]): objectList is IContent[] => { + return objectList && objectList.length !== undefined && objectList.find((o) => !isIContent(o)) === undefined || false; +}; + +export const isSavedContent = (c: ContentInternal): c is SavedContent => { + return c && isContent(c) && c.Id && c.Path && c.Path.length && c.Name && c.Name.length > 0 || false; +}; diff --git a/src/Content/Types.ts b/src/Content/Types.ts new file mode 100644 index 0000000..85d151c --- /dev/null +++ b/src/Content/Types.ts @@ -0,0 +1,7 @@ +import { IContent } from './IContent'; +import { ContentInternal } from './index'; +import { ISavedContent } from './ISavedContent'; + +export type Content = ContentInternal & T; + +export type SavedContent = ContentInternal & T & ISavedContent; diff --git a/src/Content/index.ts b/src/Content/index.ts new file mode 100644 index 0000000..04db39a --- /dev/null +++ b/src/Content/index.ts @@ -0,0 +1,5 @@ +export * from './IContent'; +export * from './ISavedContent'; +export * from './ContentInternal'; +export * from './Types'; +export * from './TypeGuards'; diff --git a/src/ContentReferences.ts b/src/ContentReferences.ts index d54d4e8..3e97eec 100644 --- a/src/ContentReferences.ts +++ b/src/ContentReferences.ts @@ -2,22 +2,21 @@ * @module Content */ /** */ - -import { isDeferred, isContentOptions, Content, isContentOptionList, SavedContent } from './Content'; -import { DeferredObject } from './ComplexTypes' -import { BaseRepository } from './Repository/BaseRepository'; -import { Observable } from '@reactivex/rxjs'; -import { IODataParams } from './ODataApi/ODataParams'; +import { Observable } from 'rxjs/Observable'; +import { DeferredObject } from './ComplexTypes'; +import { IContent, isDeferred, isIContent, SavedContent } from './Content'; import { ReferenceFieldSetting } from './FieldSettings'; +import { IODataParams } from './ODataApi/ODataParams'; import { FinializedQuery } from './Query'; -import { ContentTypes } from './SN'; +import { BaseRepository } from './Repository/BaseRepository'; +import { ContentTypes, isIContentList } from './SN'; -export abstract class ReferenceAbstract { +export abstract class ReferenceAbstract { public readonly abstract FieldSetting: ReferenceFieldSetting; public readonly abstract Repository: BaseRepository; protected _isDirty: boolean = false; - public get IsDirty(): boolean{ + public get IsDirty(): boolean { return this._isDirty; } @@ -36,31 +35,33 @@ export abstract class ReferenceAbstract { * }); * ``` */ - public Search(term: string, top: number = 10, skip: number = 0, odataParams: IODataParams = {}): FinializedQuery { - return new FinializedQuery(q => { + public Search(term: string, top: number = 10, skip: number = 0, odataParams: IODataParams = {}): FinializedQuery { + return new FinializedQuery((q) => { let query = q.Equals('_Text', `*${term}*`); if (this.FieldSetting.SelectionRoots && this.FieldSetting.SelectionRoots.length) { - query = query.And.Query(innerTree => { + query = query.And.Query((innerTree) => { this.FieldSetting.SelectionRoots && this.FieldSetting.SelectionRoots.forEach((root, index, thisArray) => { (innerTree as any) = innerTree.InTree(root); - if (index < thisArray.length - 1) + if (index < thisArray.length - 1) { innerTree = (innerTree as any).Or; + } }); return innerTree; - }) + }); } if (this.FieldSetting.AllowedTypes && this.FieldSetting.AllowedTypes.length) { - const foundTypes = this.FieldSetting.AllowedTypes.map(type => ContentTypes[type] as {new(...args: any[])}).filter(a => a !== undefined); - if (foundTypes.length > 0){ - query = query.And.Query(innerTypes => { + const foundTypes = this.FieldSetting.AllowedTypes.map((type) => ContentTypes[type] as {new(...args: any[]): T}).filter((a) => a !== undefined); + if (foundTypes.length > 0) { + query = query.And.Query((innerTypes) => { foundTypes.forEach((type, index, thisArray) => { (innerTypes as any) = innerTypes.Type(type); - if (index < thisArray.length - 1) + if (index < thisArray.length - 1) { innerTypes = (innerTypes as any).Or; - }) + } + }); return innerTypes; - }) + }); } } return query.Top(top).Skip(skip); @@ -81,7 +82,7 @@ export abstract class ReferenceAbstract { * ``` * */ -export class ContentReferenceField extends ReferenceAbstract { +export class ContentReferenceField extends ReferenceAbstract { private _contentReference: SavedContent; private _referenceUrl: string; @@ -89,7 +90,7 @@ export class ContentReferenceField extends ReferenceAbstract< * Updates the reference value to another Content * @param {T} content The new Content value */ - SetContent(content: SavedContent) { + public SetContent(content: SavedContent) { this._contentReference = content; this._isDirty = true; } @@ -99,16 +100,16 @@ export class ContentReferenceField extends ReferenceAbstract< * @param {ODataParams} odataOptions Additional options to select/expand/etc... * @returns {Observable} An observable that will publish the referenced content */ - GetContent(odataOptions?: IODataParams): Observable> { + public GetContent(odataOptions?: IODataParams): Observable> { if (this._contentReference !== undefined) { return Observable.of(this._contentReference); } const request = this.Repository.GetODataApi().Get({ path: this._referenceUrl, params: odataOptions }) - .map(r => { - return r && r.d && this.Repository.HandleLoadedContent(r.d as any); + .map((r) => { + return r && r.d && this.Repository.HandleLoadedContent(r.d); }).share(); - request.subscribe(c => { - this._contentReference = c || null; + request.subscribe((c) => { + this._contentReference = c; }); return request; @@ -117,7 +118,7 @@ export class ContentReferenceField extends ReferenceAbstract< /** * @returns The reference value (content Path) that can be used for change tracking and content updates. */ - public getValue(): string | undefined { + public GetValue(): string | undefined { return this._contentReference && this._contentReference.Path; } @@ -125,20 +126,20 @@ export class ContentReferenceField extends ReferenceAbstract< * Updates the reference URL in case of DeferredObject (not-expanded-fields) or populates the Content reference (for expanded fields) from an OData response's Field * @param {DeferredObject | T['options']} fieldData The DeferredObject or ContentOptions data that can be used */ - public handleLoaded(fieldData: DeferredObject | T['options'] & {Id: number, Path: string}) { + public HandleLoaded(fieldData: DeferredObject | SavedContent) { if (isDeferred(fieldData)) { this._referenceUrl = fieldData.__deferred.uri.replace(this.Repository.Config.ODataToken, ''); - } else if (isContentOptions(fieldData)) { - this._contentReference = this.Repository.HandleLoadedContent(fieldData); + } else if (isIContent(fieldData)) { + this._contentReference = this.Repository.HandleLoadedContent(fieldData); } this._isDirty = false; } - constructor(fieldData: DeferredObject | T['options'] & {Id: number, Path: string}, - public readonly FieldSetting: ReferenceFieldSetting, - public readonly Repository: BaseRepository) { + constructor(fieldData: DeferredObject | SavedContent, + public readonly FieldSetting: ReferenceFieldSetting, + public readonly Repository: BaseRepository) { super(); - this.handleLoaded(fieldData); + this.HandleLoaded(fieldData); } } @@ -153,7 +154,7 @@ export class ContentReferenceField extends ReferenceAbstract< * ``` * */ -export class ContentListReferenceField extends ReferenceAbstract { +export class ContentListReferenceField extends ReferenceAbstract { private _contentReferences: SavedContent[]; private _referenceUrl: string; @@ -162,31 +163,30 @@ export class ContentListReferenceField extends ReferenceAbstr * Updates the reference list to another Content list * @param {T[]} content The new list of content */ - SetContent(content: SavedContent[]) { + public SetContent(content: SavedContent[]) { this._contentReferences = content; this._isDirty = true; } - /** * Gets the current referenced values. * @param {ODataParams} odataOptions Additional options to select/expand/etc... * @returns {Observable} An observable that will publish the list of the referenced content */ - GetContent(odataOptions?: IODataParams): Observable[]> { + public GetContent(odataOptions?: IODataParams): Observable[]> { if (this._contentReferences) { return Observable.of(this._contentReferences); } // - const request = this.Repository.GetODataApi().Fetch({ + const request = this.Repository.GetODataApi().Fetch({ path: this._referenceUrl, params: odataOptions - }, Content).map(resp => { - return resp && resp.d && resp.d.results.map(c => this.Repository.HandleLoadedContent(c)) || []; + }).map((resp) => { + return resp && resp.d && resp.d.results.map((c) => this.Repository.HandleLoadedContent(c)) || []; }).share(); - request.subscribe(c => { - this._contentReferences = c + request.subscribe((c) => { + this._contentReferences = c; }); return request; @@ -195,30 +195,30 @@ export class ContentListReferenceField extends ReferenceAbstr /** * @returns The reference value (content Path list) that can be used for change tracking and content updates. */ - public getValue(): string[] | undefined { + public GetValue(): string[] | undefined { return this._contentReferences && this._contentReferences - .filter(c => c.Path && c.Path.length) - .map(c => c.Path as string); + .filter((c) => c.Path && c.Path.length) + .map((c) => c.Path as string); } /** * Updates the reference URL in case of DeferredObject (not-expanded-fields) or populates the Content list references (for expanded fields) from an OData response's field * @param {DeferredObject | T['options'][]} fieldData The DeferredObject or ContentOptions data that can be used */ - public handleLoaded(fieldData: DeferredObject | T['options'][]) { + public HandleLoaded(fieldData: DeferredObject | T[]) { if (isDeferred(fieldData)) { this._referenceUrl = fieldData.__deferred.uri.replace(this.Repository.Config.ODataToken, ''); - } else if (isContentOptionList(fieldData)) { - this._contentReferences = fieldData.map(f => this.Repository.HandleLoadedContent(f as any)); + } else if (isIContentList(fieldData)) { + this._contentReferences = fieldData.map((f) => this.Repository.HandleLoadedContent(f as SavedContent)); } this._isDirty = false; } - constructor(fieldData: DeferredObject | T['options'][], - public readonly FieldSetting: ReferenceFieldSetting, - public readonly Repository: BaseRepository) { + constructor(fieldData: DeferredObject | T[], + public readonly FieldSetting: ReferenceFieldSetting, + public readonly Repository: BaseRepository) { super(); - this.handleLoaded(fieldData); + this.HandleLoaded(fieldData); } } diff --git a/src/ContentSerializer.ts b/src/ContentSerializer.ts index 0af76fe..ea682d1 100644 --- a/src/ContentSerializer.ts +++ b/src/ContentSerializer.ts @@ -3,23 +3,23 @@ * @preferred * @description Utility to serialize and deserialize Content instances. * - * */ /** */ + */ /** */ -import { Content } from './Content'; +import { IContent, SavedContent } from './Content'; import { ODataHelper } from './SN'; /** * Represents a serialized Content instance wich can be stringified using JSON.stringify() */ -export class SerializedContent{ +export class SerializedContent { /** * The original content's field data */ - Data: T['options']; + public Data: T; /** * The full original Path for the original Content (e.g.: 'https://my.sensenet.com/OData.svc/Root/Temp/MyContent) */ - Origin: string; + public Origin: string; } export class ContentSerializer { @@ -29,15 +29,12 @@ export class ContentSerializer { * @param {Content} content The Content that needs to be serialized * @returns {SerializedContent} the SerializedContent instance */ - public static Serialize(content: T): SerializedContent { - if (!content.Path) { - throw new Error('Content Path required!'); - } + public static Serialize(content: SavedContent): SerializedContent { const repoUrl = content.GetRepository().ODataBaseUrl; return { Data: content.GetFields(true), Origin: ODataHelper.joinPaths(`${repoUrl}`, content.Path) - } + }; } /** @@ -45,14 +42,14 @@ export class ContentSerializer { * @param content The Content instance that needs to be serialized * @returns {string} The Stringified content */ - public static Stringify(content: T) { + public static Stringify(content: SavedContent) { return JSON.stringify(this.Serialize(content)); } /** * Serializes a stringified SerializedContent string to a SerializedContent instance * @param contentString the stringified SerializedContent data */ - public static Parse(contentString: string): SerializedContent { - return JSON.parse(contentString) as SerializedContent; + public static Parse(contentString: string): SerializedContent> { + return JSON.parse(contentString) as SerializedContent>; } -} \ No newline at end of file +} diff --git a/src/ContentTypes.ts b/src/ContentTypes.ts index 709828a..4db51b7 100644 --- a/src/ContentTypes.ts +++ b/src/ContentTypes.ts @@ -14,1160 +14,643 @@ * types or check the required ones. * *//** */ -import { Content, IContentOptions } from './Content'; -import { Enums, ComplexTypes } from './SN'; -import { ContentReferenceField, ContentListReferenceField} from './ContentReferences'; - - - /** - * Class representing a ContentType - * @class ContentType - * @extends {@link Content} - */ - export class ContentType extends Content { - Id?: number; - ParentId?: number; - VersionId?: number; - Name?: string; - CreatedById?: number; - ModifiedById?: number; - Version?: string; - Path?: string; - Depth?: number; - IsSystemContent?: boolean; - HandlerName?: string; - ParentTypeName?: string; - DisplayName?: string; - Description?: string; - Icon?: string; - Binary?: ComplexTypes.MediaResourceObject; - CreationDate?: string; - ModificationDate?: string; - EnableLifespan?: boolean; - - } - /** - * Interface for classes that represent a ContentType. - * @interface IContentTypeOptions - * @extends {@link IContentOptions} - */ - export interface IContentTypeOptions extends IContentOptions { - Id?: number; - ParentId?: number; - VersionId?: number; - Name?: string; - CreatedById?: number; - ModifiedById?: number; - Version?: string; - Path?: string; - Depth?: number; - IsSystemContent?: boolean; - HandlerName?: string; - ParentTypeName?: string; - DisplayName?: string; - Description?: string; - Icon?: string; - Binary?: ComplexTypes.MediaResourceObject; - CreationDate?: string; - ModificationDate?: string; - EnableLifespan?: boolean; - } - - /** - * Class representing a GenericContent - * @class GenericContent - * @extends {@link Content} - */ - export class GenericContent extends Content { - Id?: number; - ParentId?: number; - OwnerId?: number; - VersionId?: number; - Icon?: string; - Name?: string; - CreatedById?: number; - ModifiedById?: number; - Version?: string; - Path?: string; - Depth?: number; - IsSystemContent?: boolean; - IsFolder?: boolean; - DisplayName?: string; - Description?: string; - Hidden?: boolean; - Index?: number; - EnableLifespan?: boolean; - ValidFrom?: string; - ValidTill?: string; - VersioningMode?: Enums.VersioningMode; - InheritableVersioningMode?: Enums.InheritableVersioningMode; - CreationDate?: string; - ModificationDate?: string; - ApprovingMode?: Enums.ApprovingMode; - InheritableApprovingMode?: Enums.InheritableApprovingMode; - Locked?: boolean; - TrashDisabled?: boolean; - SavingState?: Enums.SavingState; - ExtensionData?: string; - BrowseApplication: ContentReferenceField; - Approvable?: boolean; - IsTaggable?: boolean; - Tags?: string; - IsRateable?: boolean; - RateStr?: string; - RateAvg?: number; - RateCount?: number; - Rate?: string; - Publishable?: boolean; - CheckInComments?: string; - RejectReason?: string; - Workspace: ContentReferenceField; - BrowseUrl?: string; - - } - /** - * Interface for classes that represent a GenericContent. - * @interface IGenericContentOptions - * @extends {@link IContentOptions} - */ - export interface IGenericContentOptions extends IContentOptions { - Id?: number; - ParentId?: number; - OwnerId?: number; - VersionId?: number; - Icon?: string; - Name?: string; - CreatedById?: number; - ModifiedById?: number; - Version?: string; - Path?: string; - Depth?: number; - IsSystemContent?: boolean; - IsFolder?: boolean; - DisplayName?: string; - Description?: string; - Hidden?: boolean; - Index?: number; - EnableLifespan?: boolean; - ValidFrom?: string; - ValidTill?: string; - VersioningMode?: Enums.VersioningMode; - InheritableVersioningMode?: Enums.InheritableVersioningMode; - CreationDate?: string; - ModificationDate?: string; - ApprovingMode?: Enums.ApprovingMode; - InheritableApprovingMode?: Enums.InheritableApprovingMode; - Locked?: boolean; - TrashDisabled?: boolean; - SavingState?: Enums.SavingState; - ExtensionData?: string; - BrowseApplication?: ContentReferenceField; - Approvable?: boolean; - IsTaggable?: boolean; - Tags?: string; - IsRateable?: boolean; - RateStr?: string; - RateAvg?: number; - RateCount?: number; - Rate?: string; - Publishable?: boolean; - CheckInComments?: string; - RejectReason?: string; - Workspace?: ContentReferenceField; - BrowseUrl?: string; - } - - /** - * Class representing a ContentLink - * @class ContentLink - * @extends {@link GenericContent} - */ - export class ContentLink extends GenericContent { - Link: ContentReferenceField; - - } - /** - * Interface for classes that represent a ContentLink. - * @interface IContentLinkOptions - * @extends {@link IGenericContentOptions} - */ - export interface IContentLinkOptions extends IGenericContentOptions { - Link?: ContentReferenceField; - } - - /** - * Class representing a File - * @class File - * @extends {@link GenericContent} - */ - export class File extends GenericContent { - Binary?: ComplexTypes.MediaResourceObject; - Size?: number; - FullSize?: number; - PageCount?: number; - MimeType?: string; - Shapes?: string; - PageAttributes?: string; - Watermark?: string; - - } - /** - * Interface for classes that represent a File. - * @interface IFileOptions - * @extends {@link IGenericContentOptions} - */ - export interface IFileOptions extends IGenericContentOptions { - Binary?: ComplexTypes.MediaResourceObject; - Size?: number; - FullSize?: number; - PageCount?: number; - MimeType?: string; - Shapes?: string; - PageAttributes?: string; - Watermark?: string; - } - - /** - * Class representing a DynamicJsonContent - * @class DynamicJsonContent - * @extends {@link File} - */ - export class DynamicJsonContent extends File { - - } - /** - * Interface for classes that represent a DynamicJsonContent. - * @interface IDynamicJsonContentOptions - * @extends {@link IFileOptions} - */ - export interface IDynamicJsonContentOptions extends IFileOptions { - } - - /** - * Class representing a ExecutableFile - * @class ExecutableFile - * @extends {@link File} - */ - export class ExecutableFile extends File { - - } - /** - * Interface for classes that represent a ExecutableFile. - * @interface IExecutableFileOptions - * @extends {@link IFileOptions} - */ - export interface IExecutableFileOptions extends IFileOptions { - } - - /** - * Class representing a HtmlTemplate - * @class HtmlTemplate - * @extends {@link File} - */ - export class HtmlTemplate extends File { - TemplateText?: string; - - } - /** - * Interface for classes that represent a HtmlTemplate. - * @interface IHtmlTemplateOptions - * @extends {@link IFileOptions} - */ - export interface IHtmlTemplateOptions extends IFileOptions { - TemplateText?: string; - } - - /** - * Class representing a Image - * @class Image - * @extends {@link File} - */ - export class Image extends File { - Keywords?: string; - DateTaken?: string; - Width?: number; - Height?: number; - - } - /** - * Interface for classes that represent a Image. - * @interface IImageOptions - * @extends {@link IFileOptions} - */ - export interface IImageOptions extends IFileOptions { - Keywords?: string; - DateTaken?: string; - Width?: number; - Height?: number; - } - - /** - * Class representing a PreviewImage - * @class PreviewImage - * @extends {@link Image} - */ - export class PreviewImage extends Image { - - } - /** - * Interface for classes that represent a PreviewImage. - * @interface IPreviewImageOptions - * @extends {@link IImageOptions} - */ - export interface IPreviewImageOptions extends IImageOptions { - } - - /** - * Class representing a Settings - * @class Settings - * @extends {@link File} - */ - export class Settings extends File { - GlobalOnly?: boolean; - - } - /** - * Interface for classes that represent a Settings. - * @interface ISettingsOptions - * @extends {@link IFileOptions} - */ - export interface ISettingsOptions extends IFileOptions { - GlobalOnly?: boolean; - } - - /** - * Class representing a IndexingSettings - * @class IndexingSettings - * @extends {@link Settings} - */ - export class IndexingSettings extends Settings { - TextExtractorInstances?: string; - - } - /** - * Interface for classes that represent a IndexingSettings. - * @interface IIndexingSettingsOptions - * @extends {@link ISettingsOptions} - */ - export interface IIndexingSettingsOptions extends ISettingsOptions { - TextExtractorInstances?: string; - } - - /** - * Class representing a LoggingSettings - * @class LoggingSettings - * @extends {@link Settings} - */ - export class LoggingSettings extends Settings { - - } - /** - * Interface for classes that represent a LoggingSettings. - * @interface ILoggingSettingsOptions - * @extends {@link ISettingsOptions} - */ - export interface ILoggingSettingsOptions extends ISettingsOptions { - } - - /** - * Class representing a PortalSettings - * @class PortalSettings - * @extends {@link Settings} - */ - export class PortalSettings extends Settings { - - } - /** - * Interface for classes that represent a PortalSettings. - * @interface IPortalSettingsOptions - * @extends {@link ISettingsOptions} - */ - export interface IPortalSettingsOptions extends ISettingsOptions { - } - - /** - * Class representing a SystemFile - * @class SystemFile - * @extends {@link File} - */ - export class SystemFile extends File { - - } - /** - * Interface for classes that represent a SystemFile. - * @interface ISystemFileOptions - * @extends {@link IFileOptions} - */ - export interface ISystemFileOptions extends IFileOptions { - } - - /** - * Class representing a Resource - * @class Resource - * @extends {@link SystemFile} - */ - export class Resource extends SystemFile { - Downloads?: number; - - } - /** - * Interface for classes that represent a Resource. - * @interface IResourceOptions - * @extends {@link ISystemFileOptions} - */ - export interface IResourceOptions extends ISystemFileOptions { - Downloads?: number; - } - - /** - * Class representing a Folder - * @class Folder - * @extends {@link GenericContent} - */ - export class Folder extends GenericContent { - - } - /** - * Interface for classes that represent a Folder. - * @interface IFolderOptions - * @extends {@link IGenericContentOptions} - */ - export interface IFolderOptions extends IGenericContentOptions { - } - - /** - * Class representing a ContentList - * @class ContentList - * @extends {@link Folder} - */ - export class ContentList extends Folder { - ContentListDefinition?: string; - DefaultView?: string; - AvailableViews: ContentListReferenceField; - FieldSettingContents: ContentListReferenceField; - AvailableContentTypeFields: ContentListReferenceField; - ListEmail?: string; - ExchangeSubscriptionId?: string; - OverwriteFiles?: boolean; - GroupAttachments?: Enums.GroupAttachments; - SaveOriginalEmail?: boolean; - IncomingEmailWorkflow: ContentReferenceField; - OnlyFromLocalGroups?: boolean; - InboxFolder?: string; - OwnerWhenVisitor: ContentReferenceField; - - } - /** - * Interface for classes that represent a ContentList. - * @interface IContentListOptions - * @extends {@link IFolderOptions} - */ - export interface IContentListOptions extends IFolderOptions { - ContentListDefinition?: string; - DefaultView?: string; - AvailableViews?: ContentListReferenceField; - FieldSettingContents?: ContentListReferenceField; - AvailableContentTypeFields?: ContentListReferenceField; - ListEmail?: string; - ExchangeSubscriptionId?: string; - OverwriteFiles?: boolean; - GroupAttachments?: Enums.GroupAttachments; - SaveOriginalEmail?: boolean; - IncomingEmailWorkflow?: ContentReferenceField; - OnlyFromLocalGroups?: boolean; - InboxFolder?: string; - OwnerWhenVisitor?: ContentReferenceField; - } - - /** - * Class representing a Aspect - * @class Aspect - * @extends {@link ContentList} - */ - export class Aspect extends ContentList { - AspectDefinition?: string; - - } - /** - * Interface for classes that represent a Aspect. - * @interface IAspectOptions - * @extends {@link IContentListOptions} - */ - export interface IAspectOptions extends IContentListOptions { - AspectDefinition?: string; - } - - /** - * Class representing a ItemList - * @class ItemList - * @extends {@link ContentList} - */ - export class ItemList extends ContentList { - - } - /** - * Interface for classes that represent a ItemList. - * @interface IItemListOptions - * @extends {@link IContentListOptions} - */ - export interface IItemListOptions extends IContentListOptions { - } - - /** - * Class representing a CustomList - * @class CustomList - * @extends {@link ItemList} - */ - export class CustomList extends ItemList { - - } - /** - * Interface for classes that represent a CustomList. - * @interface ICustomListOptions - * @extends {@link IItemListOptions} - */ - export interface ICustomListOptions extends IItemListOptions { - } - - /** - * Class representing a MemoList - * @class MemoList - * @extends {@link ItemList} - */ - export class MemoList extends ItemList { - - } - /** - * Interface for classes that represent a MemoList. - * @interface IMemoListOptions - * @extends {@link IItemListOptions} - */ - export interface IMemoListOptions extends IItemListOptions { - } - - /** - * Class representing a TaskList - * @class TaskList - * @extends {@link ItemList} - */ - export class TaskList extends ItemList { - - } - /** - * Interface for classes that represent a TaskList. - * @interface ITaskListOptions - * @extends {@link IItemListOptions} - */ - export interface ITaskListOptions extends IItemListOptions { - } - - /** - * Class representing a Library - * @class Library - * @extends {@link ContentList} - */ - export class Library extends ContentList { - - } - /** - * Interface for classes that represent a Library. - * @interface ILibraryOptions - * @extends {@link IContentListOptions} - */ - export interface ILibraryOptions extends IContentListOptions { - } - - /** - * Class representing a DocumentLibrary - * @class DocumentLibrary - * @extends {@link Library} - */ - export class DocumentLibrary extends Library { - - } - /** - * Interface for classes that represent a DocumentLibrary. - * @interface IDocumentLibraryOptions - * @extends {@link ILibraryOptions} - */ - export interface IDocumentLibraryOptions extends ILibraryOptions { - } - - /** - * Class representing a ImageLibrary - * @class ImageLibrary - * @extends {@link Library} - */ - export class ImageLibrary extends Library { - CoverImage: ContentReferenceField; - - } - /** - * Interface for classes that represent a ImageLibrary. - * @interface IImageLibraryOptions - * @extends {@link ILibraryOptions} - */ - export interface IImageLibraryOptions extends ILibraryOptions { - CoverImage?: ContentReferenceField; - } - - /** - * Class representing a Device - * @class Device - * @extends {@link Folder} - */ - export class Device extends Folder { - UserAgentPattern?: string; - - } - /** - * Interface for classes that represent a Device. - * @interface IDeviceOptions - * @extends {@link IFolderOptions} - */ - export interface IDeviceOptions extends IFolderOptions { - UserAgentPattern?: string; - } - - /** - * Class representing a Domain - * @class Domain - * @extends {@link Folder} - */ - export class Domain extends Folder { - SyncGuid?: string; - LastSync?: string; - - } - /** - * Interface for classes that represent a Domain. - * @interface IDomainOptions - * @extends {@link IFolderOptions} - */ - export interface IDomainOptions extends IFolderOptions { - SyncGuid?: string; - LastSync?: string; - } - - /** - * Class representing a Domains - * @class Domains - * @extends {@link Folder} - */ - export class Domains extends Folder { - - } - /** - * Interface for classes that represent a Domains. - * @interface IDomainsOptions - * @extends {@link IFolderOptions} - */ - export interface IDomainsOptions extends IFolderOptions { - } - - /** - * Class representing a Email - * @class Email - * @extends {@link Folder} - */ - export class Email extends Folder { - From?: string; - Body?: string; - Sent?: string; - - } - /** - * Interface for classes that represent a Email. - * @interface IEmailOptions - * @extends {@link IFolderOptions} - */ - export interface IEmailOptions extends IFolderOptions { - From?: string; - Body?: string; - Sent?: string; - } - - /** - * Class representing a OrganizationalUnit - * @class OrganizationalUnit - * @extends {@link Folder} - */ - export class OrganizationalUnit extends Folder { - SyncGuid?: string; - LastSync?: string; - - } - /** - * Interface for classes that represent a OrganizationalUnit. - * @interface IOrganizationalUnitOptions - * @extends {@link IFolderOptions} - */ - export interface IOrganizationalUnitOptions extends IFolderOptions { - SyncGuid?: string; - LastSync?: string; - } - - /** - * Class representing a PortalRoot - * @class PortalRoot - * @extends {@link Folder} - */ - export class PortalRoot extends Folder { - - } - /** - * Interface for classes that represent a PortalRoot. - * @interface IPortalRootOptions - * @extends {@link IFolderOptions} - */ - export interface IPortalRootOptions extends IFolderOptions { - } - - /** - * Class representing a ProfileDomain - * @class ProfileDomain - * @extends {@link Folder} - */ - export class ProfileDomain extends Folder { - - } - /** - * Interface for classes that represent a ProfileDomain. - * @interface IProfileDomainOptions - * @extends {@link IFolderOptions} - */ - export interface IProfileDomainOptions extends IFolderOptions { - } - - /** - * Class representing a Profiles - * @class Profiles - * @extends {@link Folder} - */ - export class Profiles extends Folder { - - } - /** - * Interface for classes that represent a Profiles. - * @interface IProfilesOptions - * @extends {@link IFolderOptions} - */ - export interface IProfilesOptions extends IFolderOptions { - } - - /** - * Class representing a RuntimeContentContainer - * @class RuntimeContentContainer - * @extends {@link Folder} - */ - export class RuntimeContentContainer extends Folder { - - } - /** - * Interface for classes that represent a RuntimeContentContainer. - * @interface IRuntimeContentContainerOptions - * @extends {@link IFolderOptions} - */ - export interface IRuntimeContentContainerOptions extends IFolderOptions { - } - - /** - * Class representing a Sites - * @class Sites - * @extends {@link Folder} - */ - export class Sites extends Folder { - - } - /** - * Interface for classes that represent a Sites. - * @interface ISitesOptions - * @extends {@link IFolderOptions} - */ - export interface ISitesOptions extends IFolderOptions { - } - - /** - * Class representing a SmartFolder - * @class SmartFolder - * @extends {@link Folder} - */ - export class SmartFolder extends Folder { - Query?: string; - EnableAutofilters?: Enums.EnableAutofilters; - EnableLifespanFilter?: Enums.EnableLifespanFilter; - - } - /** - * Interface for classes that represent a SmartFolder. - * @interface ISmartFolderOptions - * @extends {@link IFolderOptions} - */ - export interface ISmartFolderOptions extends IFolderOptions { - Query?: string; - EnableAutofilters?: Enums.EnableAutofilters; - EnableLifespanFilter?: Enums.EnableLifespanFilter; - } - - /** - * Class representing a SystemFolder - * @class SystemFolder - * @extends {@link Folder} - */ - export class SystemFolder extends Folder { - - } - /** - * Interface for classes that represent a SystemFolder. - * @interface ISystemFolderOptions - * @extends {@link IFolderOptions} - */ - export interface ISystemFolderOptions extends IFolderOptions { - } - - /** - * Class representing a Resources - * @class Resources - * @extends {@link SystemFolder} - */ - export class Resources extends SystemFolder { - - } - /** - * Interface for classes that represent a Resources. - * @interface IResourcesOptions - * @extends {@link ISystemFolderOptions} - */ - export interface IResourcesOptions extends ISystemFolderOptions { - } - - /** - * Class representing a TrashBag - * @class TrashBag - * @extends {@link Folder} - */ - export class TrashBag extends Folder { - KeepUntil?: string; - OriginalPath?: string; - WorkspaceRelativePath?: string; - WorkspaceId?: number; - DeletedContent: ContentReferenceField; - - } - /** - * Interface for classes that represent a TrashBag. - * @interface ITrashBagOptions - * @extends {@link IFolderOptions} - */ - export interface ITrashBagOptions extends IFolderOptions { - KeepUntil?: string; - OriginalPath?: string; - WorkspaceRelativePath?: string; - WorkspaceId?: number; - DeletedContent?: ContentReferenceField; - } - - /** - * Class representing a Workspace - * @class Workspace - * @extends {@link Folder} - */ - export class Workspace extends Folder { - Manager: ContentReferenceField; - Deadline?: string; - IsActive?: boolean; - WorkspaceSkin: ContentReferenceField; - IsCritical?: boolean; - IsWallContainer?: boolean; - IsFollowed?: boolean; - - } - /** - * Interface for classes that represent a Workspace. - * @interface IWorkspaceOptions - * @extends {@link IFolderOptions} - */ - export interface IWorkspaceOptions extends IFolderOptions { - Manager?: ContentReferenceField; - Deadline?: string; - IsActive?: boolean; - WorkspaceSkin?: ContentReferenceField; - IsCritical?: boolean; - IsWallContainer?: boolean; - IsFollowed?: boolean; - } - - /** - * Class representing a Site - * @class Site - * @extends {@link Workspace} - */ - export class Site extends Workspace { - Language?: Enums.Language; - EnableClientBasedCulture?: boolean; - EnableUserBasedCulture?: boolean; - UrlList?: string; - StartPage: ContentReferenceField; - LoginPage: ContentReferenceField; - SiteSkin: ContentReferenceField; - DenyCrossSiteAccess?: boolean; - - } - /** - * Interface for classes that represent a Site. - * @interface ISiteOptions - * @extends {@link IWorkspaceOptions} - */ - export interface ISiteOptions extends IWorkspaceOptions { - Language?: Enums.Language; - EnableClientBasedCulture?: boolean; - EnableUserBasedCulture?: boolean; - UrlList?: string; - StartPage?: ContentReferenceField; - LoginPage?: ContentReferenceField; - SiteSkin?: ContentReferenceField; - DenyCrossSiteAccess?: boolean; - } - - /** - * Class representing a TrashBin - * @class TrashBin - * @extends {@link Workspace} - */ - export class TrashBin extends Workspace { - MinRetentionTime?: number; - SizeQuota?: number; - BagCapacity?: number; - - } - /** - * Interface for classes that represent a TrashBin. - * @interface ITrashBinOptions - * @extends {@link IWorkspaceOptions} - */ - export interface ITrashBinOptions extends IWorkspaceOptions { - MinRetentionTime?: number; - SizeQuota?: number; - BagCapacity?: number; - } - - /** - * Class representing a UserProfile - * @class UserProfile - * @extends {@link Workspace} - */ - export class UserProfile extends Workspace { - User: ContentReferenceField; - - } - /** - * Interface for classes that represent a UserProfile. - * @interface IUserProfileOptions - * @extends {@link IWorkspaceOptions} - */ - export interface IUserProfileOptions extends IWorkspaceOptions { - User?: ContentReferenceField; - } - - /** - * Class representing a Group - * @class Group - * @extends {@link GenericContent} - */ - export class Group extends GenericContent { - Members: ContentListReferenceField; - SyncGuid?: string; - LastSync?: string; - - } - /** - * Interface for classes that represent a Group. - * @interface IGroupOptions - * @extends {@link IGenericContentOptions} - */ - export interface IGroupOptions extends IGenericContentOptions { - Members?: ContentListReferenceField; - SyncGuid?: string; - LastSync?: string; - } - - /** - * Class representing a ListItem - * @class ListItem - * @extends {@link GenericContent} - */ - export class ListItem extends GenericContent { - - } - /** - * Interface for classes that represent a ListItem. - * @interface IListItemOptions - * @extends {@link IGenericContentOptions} - */ - export interface IListItemOptions extends IGenericContentOptions { - } - - /** - * Class representing a CustomListItem - * @class CustomListItem - * @extends {@link ListItem} - */ - export class CustomListItem extends ListItem { - WorkflowsRunning?: boolean; - - } - /** - * Interface for classes that represent a CustomListItem. - * @interface ICustomListItemOptions - * @extends {@link IListItemOptions} - */ - export interface ICustomListItemOptions extends IListItemOptions { - WorkflowsRunning?: boolean; - } - - /** - * Class representing a Memo - * @class Memo - * @extends {@link ListItem} - */ - export class Memo extends ListItem { - Date?: string; - MemoType?: Enums.MemoType; - SeeAlso: ContentListReferenceField; - - } - /** - * Interface for classes that represent a Memo. - * @interface IMemoOptions - * @extends {@link IListItemOptions} - */ - export interface IMemoOptions extends IListItemOptions { - Date?: string; - MemoType?: Enums.MemoType; - SeeAlso?: ContentListReferenceField; - } - - /** - * Class representing a Task - * @class Task - * @extends {@link ListItem} - */ - export class Task extends ListItem { - StartDate?: string; - DueDate?: string; - AssignedTo: ContentListReferenceField; - Priority?: Enums.Priority; - Status?: Enums.Status; - TaskCompletion?: number; - RemainingDays?: number; - DueText?: string; - DueCssClass?: string; - - } - /** - * Interface for classes that represent a Task. - * @interface ITaskOptions - * @extends {@link IListItemOptions} - */ - export interface ITaskOptions extends IListItemOptions { - StartDate?: string; - DueDate?: string; - AssignedTo?: ContentListReferenceField; - Priority?: Enums.Priority; - Status?: Enums.Status; - TaskCompletion?: number; - RemainingDays?: number; - DueText?: string; - DueCssClass?: string; - } - - /** - * Class representing a Query - * @class Query - * @extends {@link GenericContent} - */ - export class Query extends GenericContent { - Query?: string; - QueryType?: Enums.QueryType; - - } - /** - * Interface for classes that represent a Query. - * @interface IQueryOptions - * @extends {@link IGenericContentOptions} - */ - export interface IQueryOptions extends IGenericContentOptions { - Query?: string; - QueryType?: Enums.QueryType; - } - - /** - * Class representing a User - * @class User - * @extends {@link GenericContent} - */ - export class User extends GenericContent { - LoginName?: string; - JobTitle?: string; - Enabled?: boolean; - Domain?: string; - Email?: string; - FullName?: string; - ImageRef: ContentReferenceField; - ImageData?: ComplexTypes.MediaResourceObject; - Avatar?: ComplexTypes.MediaResourceObject; - Password?: string; - SyncGuid?: string; - LastSync?: string; - Captcha?: string; - Manager: ContentReferenceField; - Department?: string; - Languages?: string; - Phone?: string; - Gender?: Enums.Gender; - MaritalStatus?: Enums.MaritalStatus; - BirthDate?: string; - Education?: string; - TwitterAccount?: string; - FacebookURL?: string; - LinkedInURL?: string; - Language?: Enums.Language; - FollowedWorkspaces: ContentListReferenceField; - ProfilePath?: string; - - } - /** - * Interface for classes that represent a User. - * @interface IUserOptions - * @extends {@link IGenericContentOptions} - */ - export interface IUserOptions extends IGenericContentOptions { - LoginName?: string; - JobTitle?: string; - Enabled?: boolean; - Domain?: string; - Email?: string; - FullName?: string; - ImageRef?: ContentReferenceField; - ImageData?: ComplexTypes.MediaResourceObject; - Avatar?: ComplexTypes.MediaResourceObject; - Password?: string; - SyncGuid?: string; - LastSync?: string; - Captcha?: string; - Manager?: ContentReferenceField; - Department?: string; - Languages?: string; - Phone?: string; - Gender?: Enums.Gender; - MaritalStatus?: Enums.MaritalStatus; - BirthDate?: string; - Education?: string; - TwitterAccount?: string; - FacebookURL?: string; - LinkedInURL?: string; - Language?: Enums.Language; - FollowedWorkspaces?: ContentListReferenceField; - ProfilePath?: string; - } +// tslint:disable:naming-convention + + import { BinaryField } from './BinaryField'; + import { ContentListReferenceField, ContentReferenceField } from './ContentReferences'; + import { ComplexTypes, Enums } from './SN'; + + /** + * Class representing a ContentType + * @class ContentType + */ + export class ContentType { + public Id?: number; + public ParentId?: number; + public VersionId?: number; + public Name?: string; + public CreatedById?: number; + public ModifiedById?: number; + public Version?: string; + public Path?: string; + public Depth?: number; + public IsSystemContent?: boolean; + public HandlerName?: string; + public ParentTypeName?: string; + public DisplayName?: string; + public Description?: string; + public Icon?: string; + public Binary?: ComplexTypes.MediaResourceObject; + public CreatedBy?: ContentReferenceField; + public CreationDate?: string; + public ModifiedBy?: ContentReferenceField; + public ModificationDate?: string; + public EnableLifespan?: boolean; + + } + + /** + * Class representing a GenericContent + * @class GenericContent + */ + export class GenericContent { + public Id?: number; + public ParentId?: number; + public OwnerId?: number; + public Owner?: ContentReferenceField; + public VersionId?: number; + public Icon?: string; + public Name?: string; + public CreatedById?: number; + public ModifiedById?: number; + public Version?: string; + public Path?: string; + public Depth?: number; + public IsSystemContent?: boolean; + public IsFolder?: boolean; + public DisplayName?: string; + public Description?: string; + public Hidden?: boolean; + public Index?: number; + public EnableLifespan?: boolean; + public ValidFrom?: string; + public ValidTill?: string; + public AllowedChildTypes?: string; + public EffectiveAllowedChildTypes?: string; + public VersioningMode?: Enums.VersioningMode; + public InheritableVersioningMode?: Enums.InheritableVersioningMode; + public CreatedBy?: ContentReferenceField; + public CreationDate?: string; + public ModifiedBy?: ContentReferenceField; + public ModificationDate?: string; + public ApprovingMode?: Enums.ApprovingMode; + public InheritableApprovingMode?: Enums.InheritableApprovingMode; + public Locked?: boolean; + public CheckedOutTo?: ContentReferenceField; + public TrashDisabled?: boolean; + public SavingState?: Enums.SavingState; + public ExtensionData?: string; + public BrowseApplication?: ContentReferenceField; + public Approvable?: boolean; + public IsTaggable?: boolean; + public Tags?: string; + public IsRateable?: boolean; + public RateStr?: string; + public RateAvg?: number; + public RateCount?: number; + public Rate?: string; + public Publishable?: boolean; + public Versions?: ContentListReferenceField; + public CheckInComments?: string; + public RejectReason?: string; + public Workspace?: ContentReferenceField; + public BrowseUrl?: string; + public Type?: string; + + } + + /** + * Class representing a ContentLink + * @class ContentLink + * @extends {@link GenericContent} + */ + export class ContentLink extends GenericContent { + public Link?: ContentReferenceField; + + } + + /** + * Class representing a File + * @class File + * @extends {@link GenericContent} + */ + export class File extends GenericContent { + public Binary: BinaryField; + public Size?: number; + public FullSize?: number; + public PageCount?: number; + public MimeType?: string; + public Shapes?: string; + public PageAttributes?: string; + public Watermark?: string; + + } + + /** + * Class representing a DynamicJsonContent + * @class DynamicJsonContent + * @extends {@link File} + */ + export class DynamicJsonContent extends File { + + } + + /** + * Class representing a ExecutableFile + * @class ExecutableFile + * @extends {@link File} + */ + export class ExecutableFile extends File { + + } + + /** + * Class representing a HtmlTemplate + * @class HtmlTemplate + * @extends {@link File} + */ + export class HtmlTemplate extends File { + public TemplateText?: string; + + } + + /** + * Class representing a Image + * @class Image + * @extends {@link File} + */ + export class Image extends File { + public Keywords?: string; + public DateTaken?: string; + public Width?: number; + public Height?: number; + + } + + /** + * Class representing a PreviewImage + * @class PreviewImage + * @extends {@link Image} + */ + export class PreviewImage extends Image { + + } + + /** + * Class representing a Settings + * @class Settings + * @extends {@link File} + */ + export class Settings extends File { + public GlobalOnly?: boolean; + + } + + /** + * Class representing a IndexingSettings + * @class IndexingSettings + * @extends {@link Settings} + */ + export class IndexingSettings extends Settings { + public TextExtractorInstances?: string; + + } + + /** + * Class representing a LoggingSettings + * @class LoggingSettings + * @extends {@link Settings} + */ + export class LoggingSettings extends Settings { + + } + + /** + * Class representing a PortalSettings + * @class PortalSettings + * @extends {@link Settings} + */ + export class PortalSettings extends Settings { + + } + + /** + * Class representing a SystemFile + * @class SystemFile + * @extends {@link File} + */ + export class SystemFile extends File { + + } + + /** + * Class representing a Resource + * @class Resource + * @extends {@link SystemFile} + */ + export class Resource extends SystemFile { + public Downloads?: number; + + } + + /** + * Class representing a Folder + * @class Folder + * @extends {@link GenericContent} + */ + export class Folder extends GenericContent { + + } + + /** + * Class representing a ContentList + * @class ContentList + * @extends {@link Folder} + */ + export class ContentList extends Folder { + public ContentListDefinition?: string; + public DefaultView?: string; + // AvailableViews?: ContentListReferenceField; + // FieldSettingContents?: ContentListReferenceField; + // AvailableContentTypeFields?: ContentListReferenceField; + public ListEmail?: string; + public ExchangeSubscriptionId?: string; + public OverwriteFiles?: boolean; + public GroupAttachments?: Enums.GroupAttachments; + public SaveOriginalEmail?: boolean; + public IncomingEmailWorkflow?: ContentReferenceField; + public OnlyFromLocalGroups?: boolean; + public InboxFolder?: string; + public OwnerWhenVisitor?: ContentReferenceField; + + } + + /** + * Class representing a Aspect + * @class Aspect + * @extends {@link ContentList} + */ + export class Aspect extends ContentList { + public AspectDefinition?: string; + + } + + /** + * Class representing a ItemList + * @class ItemList + * @extends {@link ContentList} + */ + export class ItemList extends ContentList { + + } + + /** + * Class representing a CustomList + * @class CustomList + * @extends {@link ItemList} + */ + export class CustomList extends ItemList { + + } + + /** + * Class representing a MemoList + * @class MemoList + * @extends {@link ItemList} + */ + export class MemoList extends ItemList { + + } + + /** + * Class representing a TaskList + * @class TaskList + * @extends {@link ItemList} + */ + export class TaskList extends ItemList { + + } + + /** + * Class representing a Library + * @class Library + * @extends {@link ContentList} + */ + export class Library extends ContentList { + + } + + /** + * Class representing a DocumentLibrary + * @class DocumentLibrary + * @extends {@link Library} + */ + export class DocumentLibrary extends Library { + + } + + /** + * Class representing a ImageLibrary + * @class ImageLibrary + * @extends {@link Library} + */ + export class ImageLibrary extends Library { + public CoverImage?: ContentReferenceField; + + } + + /** + * Class representing a Device + * @class Device + * @extends {@link Folder} + */ + export class Device extends Folder { + public UserAgentPattern?: string; + + } + + /** + * Class representing a Domain + * @class Domain + * @extends {@link Folder} + */ + export class Domain extends Folder { + public SyncGuid?: string; + public LastSync?: string; + + } + + /** + * Class representing a Domains + * @class Domains + * @extends {@link Folder} + */ + export class Domains extends Folder { + + } + + /** + * Class representing a Email + * @class Email + * @extends {@link Folder} + */ + export class Email extends Folder { + public From?: string; + public Body?: string; + public Sent?: string; + + } + + /** + * Class representing a OrganizationalUnit + * @class OrganizationalUnit + * @extends {@link Folder} + */ + export class OrganizationalUnit extends Folder { + public SyncGuid?: string; + public LastSync?: string; + + } + + /** + * Class representing a PortalRoot + * @class PortalRoot + * @extends {@link Folder} + */ + export class PortalRoot extends Folder { + + } + + /** + * Class representing a ProfileDomain + * @class ProfileDomain + * @extends {@link Folder} + */ + export class ProfileDomain extends Folder { + + } + + /** + * Class representing a Profiles + * @class Profiles + * @extends {@link Folder} + */ + export class Profiles extends Folder { + + } + + /** + * Class representing a RuntimeContentContainer + * @class RuntimeContentContainer + * @extends {@link Folder} + */ + export class RuntimeContentContainer extends Folder { + + } + + /** + * Class representing a Sites + * @class Sites + * @extends {@link Folder} + */ + export class Sites extends Folder { + + } + + /** + * Class representing a SmartFolder + * @class SmartFolder + * @extends {@link Folder} + */ + export class SmartFolder extends Folder { + public Query?: string; + public EnableAutofilters?: Enums.EnableAutofilters; + public EnableLifespanFilter?: Enums.EnableLifespanFilter; + + } + + /** + * Class representing a SystemFolder + * @class SystemFolder + * @extends {@link Folder} + */ + export class SystemFolder extends Folder { + + } + + /** + * Class representing a Resources + * @class Resources + * @extends {@link SystemFolder} + */ + export class Resources extends SystemFolder { + + } + + /** + * Class representing a TrashBag + * @class TrashBag + * @extends {@link Folder} + */ + export class TrashBag extends Folder { + public KeepUntil?: string; + public OriginalPath?: string; + public WorkspaceRelativePath?: string; + public WorkspaceId?: number; + public DeletedContent?: ContentReferenceField; + + } + + /** + * Class representing a Workspace + * @class Workspace + * @extends {@link Folder} + */ + export class Workspace extends Folder { + public Manager?: ContentReferenceField; + public Deadline?: string; + public IsActive?: boolean; + // WorkspaceSkin?: ContentReferenceField; + public IsCritical?: boolean; + public IsWallContainer?: boolean; + public IsFollowed?: boolean; + + } + + /** + * Class representing a Site + * @class Site + * @extends {@link Workspace} + */ + export class Site extends Workspace { + public Language?: Enums.Language; + public EnableClientBasedCulture?: boolean; + public EnableUserBasedCulture?: boolean; + public UrlList?: string; + public StartPage?: ContentReferenceField; + public LoginPage?: ContentReferenceField; + // SiteSkin?: ContentReferenceField; + public DenyCrossSiteAccess?: boolean; + + } + + /** + * Class representing a TrashBin + * @class TrashBin + * @extends {@link Workspace} + */ + export class TrashBin extends Workspace { + public MinRetentionTime?: number; + public SizeQuota?: number; + public BagCapacity?: number; + + } + + /** + * Class representing a UserProfile + * @class UserProfile + * @extends {@link Workspace} + */ + export class UserProfile extends Workspace { + public User?: ContentReferenceField; + + } + + /** + * Class representing a Group + * @class Group + * @extends {@link GenericContent} + */ + export class Group extends GenericContent { + public Members?: ContentListReferenceField; + public SyncGuid?: string; + public LastSync?: string; + + } + + /** + * Class representing a ListItem + * @class ListItem + * @extends {@link GenericContent} + */ + export class ListItem extends GenericContent { + + } + + /** + * Class representing a CustomListItem + * @class CustomListItem + * @extends {@link ListItem} + */ + export class CustomListItem extends ListItem { + public WorkflowsRunning?: boolean; + + } + + /** + * Class representing a Memo + * @class Memo + * @extends {@link ListItem} + */ + export class Memo extends ListItem { + public Date?: string; + public MemoType?: Enums.MemoType; + public SeeAlso?: ContentListReferenceField; + + } + + /** + * Class representing a Task + * @class Task + * @extends {@link ListItem} + */ + export class Task extends ListItem { + public StartDate?: string; + public DueDate?: string; + public AssignedTo?: ContentListReferenceField; + public Priority?: Enums.Priority; + public Status?: Enums.Status; + public TaskCompletion?: number; + public RemainingDays?: number; + public DueText?: string; + public DueCssClass?: string; + + } + + /** + * Class representing a Query + * @class Query + * @extends {@link GenericContent} + */ + export class Query extends GenericContent { + public Query?: string; + public QueryType?: Enums.QueryType; + + } + + /** + * Class representing a User + * @class User + * @extends {@link GenericContent} + */ + export class User extends GenericContent { + public LoginName?: string; + public JobTitle?: string; + public Enabled?: boolean; + public Domain?: string; + public Email?: string; + public FullName?: string; + public ImageRef?: ContentReferenceField; + public ImageData?: ComplexTypes.MediaResourceObject; + public Avatar?: ComplexTypes.DeferredObject; + public Password?: string; + public SyncGuid?: string; + public LastSync?: string; + public Captcha?: string; + public Manager?: ContentReferenceField; + public Department?: string; + public Languages?: string; + public Phone?: string; + public Gender?: Enums.Gender; + public MaritalStatus?: Enums.MaritalStatus; + public BirthDate?: string; + public Education?: string; + public TwitterAccount?: string; + public FacebookURL?: string; + public LinkedInURL?: string; + public Language?: Enums.Language; + public FollowedWorkspaces?: ContentListReferenceField; + public ProfilePath?: string; + + } diff --git a/src/ControlMapper.ts b/src/ControlMapper.ts index 9223172..163f2d6 100644 --- a/src/ControlMapper.ts +++ b/src/ControlMapper.ts @@ -23,25 +23,27 @@ * ``` */ /** */ -import { Content } from './Content'; +import { IContent } from './Content'; import * as FieldSettings from './FieldSettings'; +import { BaseRepository } from './Repository/index'; import { Schemas } from './SN'; export type ActionName = 'new' | 'edit' | 'view'; export class ControlSchema { - ContentTypeControl: {new(...args: any[]): TControlBaseType}; - Schema: Schemas.Schema; - FieldMappings: {FieldSettings: FieldSettings.FieldSetting, ControlType: {new(...args: any[]): TControlBaseType}, ClientSettings: TClientControlSettings}[]; + public ContentTypeControl: { new(...args: any[]): TControlBaseType }; + public Schema: Schemas.Schema; + public FieldMappings: { FieldSettings: FieldSettings.FieldSetting, ControlType: { new(...args: any[]): TControlBaseType }, ClientSettings: TClientControlSettings }[]; } export class ControlMapper { constructor( - public readonly controlBaseType: { new (...args: any[]): TControlBaseType }, + private readonly _repository: BaseRepository, + public readonly ControlBaseType: { new(...args: any[]): TControlBaseType }, private readonly _clientControlSettingsFactory: (fieldSetting: FieldSettings.FieldSetting) => TClientControlSettings, - private readonly _defaultControlType?: { new (...args: any[]): TControlBaseType }, - private readonly _defaultFieldSettingControlType?: { new (...args: any[]): TControlBaseType }, + private readonly _defaultControlType?: { new(...args: any[]): TControlBaseType }, + private readonly _defaultFieldSettingControlType?: { new(...args: any[]): TControlBaseType }, ) { } @@ -50,11 +52,11 @@ export class ControlMapper { * @param contentType The type of the content (e.g. ContentTypes.Task) * @param actionName The name of the action. Can be 'new' / 'view' / 'edit' */ - private getTypeSchema(contentType: { new (args: any[]): TContentType }, actionName: ActionName): Schemas.Schema { - const schema = new Schemas.Schema(Content.GetSchema(contentType)); + private getTypeSchema(contentType: { new(args: any[]): TContentType }, actionName: ActionName): Schemas.Schema { + const schema = this._repository.GetSchema(contentType); if (actionName) { - schema.FieldSettings = schema.FieldSettings.filter(s => { + schema.FieldSettings = schema.FieldSettings.filter((s) => { switch (actionName) { case 'new': return s.VisibleNew === FieldSettings.FieldVisibility.Show; @@ -63,12 +65,12 @@ export class ControlMapper { case 'view': return s.VisibleBrowse === FieldSettings.FieldVisibility.Show; } - }) + }); } return schema; } - private _contentTypeControlMaps: { new (...args: any[]): TControlBaseType }[] = []; + private _contentTypeControlMaps: { new(...args: any[]): TControlBaseType }[] = []; /** * Maps a specified Control to a Content type @@ -76,8 +78,8 @@ export class ControlMapper { * @param control The Control for the content * @returns {ControlMapper} */ - public MapContentTypeToControl(contentType: { new (...args: any[]): Content }, control: { new (...args: any[]): TControlBaseType }) { - this._contentTypeControlMaps[contentType.name as any] = control; + public MapContentTypeToControl(contentType: { new(...args: any[]): IContent }, control: { new(...args: any[]): TControlBaseType }) { + this._contentTypeControlMaps[contentType.name] = control; return this; } @@ -86,13 +88,11 @@ export class ControlMapper { * @param content The content to get the control for. * @returns {TControlBaseType} The mapped control, Default if nothing is mapped. */ - public GetControlForContentType(contentType: { new (...args: any[]): TContentType }) { - return this._contentTypeControlMaps[contentType.name as any] || this._defaultControlType; + public GetControlForContentType(contentType: { new(...args: any[]): TContentType }) { + return this._contentTypeControlMaps[contentType.name] || this._defaultControlType; } - - private _fieldSettingDefaults: ((fieldSetting: FieldSettings.FieldSetting) => { new (...args: any[]): TControlBaseType })[] = []; - + private _fieldSettingDefaults: Map { new(...args: any[]): TControlBaseType })> = new Map(); /** * @@ -101,10 +101,10 @@ export class ControlMapper { * @returns the Mapper instance (can be used fluently) */ public SetupFieldSettingDefault( - fieldSetting: { new (...args: any[]): TFieldSettingType }, - setupControl: (fieldSetting: TFieldSettingType) => { new (...args: any[]): TControlBaseType } + fieldSetting: { new(...args: any[]): TFieldSettingType }, + setupControl: (fieldSetting: TFieldSettingType) => { new(...args: any[]): TControlBaseType } ) { - this._fieldSettingDefaults[fieldSetting.name as any] = setupControl; + this._fieldSettingDefaults.set(fieldSetting.name, setupControl as any); return this; } @@ -112,12 +112,12 @@ export class ControlMapper { * @returns {TControlBaseType} The specified FieldSetting control * @param fieldSetting The FieldSetting to get the control class. */ - public GetControlForFieldSetting(fieldSetting: TFieldSettingType): { new (...args: any[]): TControlBaseType } { - const fieldSettingSetup = this._fieldSettingDefaults[fieldSetting.constructor.name as any]; + public GetControlForFieldSetting(fieldSetting: TFieldSettingType): { new(...args: any[]): TControlBaseType } { + const fieldSettingSetup = this._fieldSettingDefaults.get(fieldSetting.Type) as any as (fieldSetting) => { new(...args: any[]): TControlBaseType }; return fieldSettingSetup && fieldSettingSetup(fieldSetting) || this._defaultFieldSettingControlType; } - private _contentTypeBoundfieldSettings: ((fieldSetting: FieldSettings.FieldSetting) => { new (...args: any[]): TControlBaseType })[] = []; + private _contentTypeBoundfieldSettings: Map { new(...args: any[]): TControlBaseType })> = new Map(); /** * @@ -127,18 +127,17 @@ export class ControlMapper { * @param fieldSetting Optional type hint for the FieldSetting */ - public SetupFieldSettingForControl( - contentType: { new (...args: any[]): TContentType }, + public SetupFieldSettingForControl( + contentType: { new(...args: any[]): TContentType }, fieldName: TField, - setupControl: (fieldSetting: TFieldSettingType) => { new (...args: any[]): TControlBaseType }, - fieldSetting?: { new (...args: any[]): TFieldSettingType }, + setupControl: (fieldSetting: TFieldSettingType) => { new(...args: any[]): TControlBaseType }, + fieldSetting?: { new(...args: any[]): TFieldSettingType }, ) { - this._contentTypeBoundfieldSettings[`${contentType.name}-${fieldName}` as any] = setupControl; + this._contentTypeBoundfieldSettings.set(`${contentType.name}-${fieldName}`, setupControl as any); return this; } - /** * * @param contentType The type of the content (e.g. ContentTypes.Task) @@ -146,31 +145,30 @@ export class ControlMapper { * @param actionName The name of the Action (can be 'new' / 'edit' / 'view') * @returns The assigned Control constructor or the default Field control */ - public GetControlForContentField( - contentType: { new (...args: any[]): TContentType }, + public GetControlForContentField( + contentType: { new(...args: any[]): TContentType }, fieldName: TField, actionName: ActionName - ): {new(...args: any[]): TControlBaseType} { + ): { new(...args: any[]): TControlBaseType } { - const fieldSetting = this.getTypeSchema(contentType, actionName).FieldSettings.filter(s => s.Name === fieldName)[0]; + const fieldSetting = this.getTypeSchema(contentType, actionName).FieldSettings.filter((s) => s.Name === fieldName)[0]; - if (this._contentTypeBoundfieldSettings[`${contentType.name}-${fieldName}` as any]) { - return this._contentTypeBoundfieldSettings[`${contentType.name}-${fieldName}` as any](fieldSetting); - } - else { + if (this._contentTypeBoundfieldSettings.has(`${contentType.name}-${fieldName}`)) { + return (this._contentTypeBoundfieldSettings.get(`${contentType.name}-${fieldName}`) as any)(fieldSetting); + } else { return this.GetControlForFieldSetting(fieldSetting); } } - private _fieldSettingBoundClientSettingFactories: ((setting: FieldSettings.FieldSetting) => TClientControlSettings)[] = [] + private _fieldSettingBoundClientSettingFactories: Map TClientControlSettings)> = new Map(); /** * Sets up a Factory method to create library-specific settings from FieldSettings per type * @param fieldSettingType The type of the FieldSetting (e.g. FieldSettings.ShortTextFieldSetting) * @param factoryMethod The factory method that constructs or transforms the Settings object */ - public SetClientControlFactory(fieldSettingType: {new(...args: any[]): TFieldSetting}, factoryMethod: (setting: TFieldSetting) => TClientControlSettings){ - this._fieldSettingBoundClientSettingFactories[fieldSettingType.name as any] = factoryMethod; + public SetClientControlFactory(fieldSettingType: { new(...args: any[]): TFieldSetting }, factoryMethod: (setting: TFieldSetting) => TClientControlSettings) { + this._fieldSettingBoundClientSettingFactories.set(fieldSettingType.name, factoryMethod as any); return this; } @@ -179,8 +177,8 @@ export class ControlMapper { * @param fieldSetting The FieldSetting object that should be used for creating the new Setting entry * @returns the created or transformed Client Setting */ - public CreateClientSetting(fieldSetting: TFieldSetting){ - const factoryMethod = this._fieldSettingBoundClientSettingFactories[fieldSetting.constructor.name as any] || this._clientControlSettingsFactory; + public CreateClientSetting(fieldSetting: TFieldSetting) { + const factoryMethod = this._fieldSettingBoundClientSettingFactories.get(fieldSetting.Type) || this._clientControlSettingsFactory; return factoryMethod(fieldSetting); } @@ -190,14 +188,14 @@ export class ControlMapper { * @param actionName The name of the Action (can be 'new' / 'edit' / 'view') * @returns the fully created ControlSchema */ - public GetFullSchemaForContentType( - contentType: { new (...args: any[]): TContentType }, + public GetFullSchemaForContentType( + contentType: { new(...args: any[]): TContentType }, actionName: ActionName): ControlSchema { const schema = this.getTypeSchema(contentType, actionName); - const mappings = schema.FieldSettings.map(f => { + const mappings = schema.FieldSettings.map((f) => { const clientSetting: TClientControlSettings = this.CreateClientSetting(f); - const control: {new(...args: any[]): TControlBaseType} = this.GetControlForContentField(contentType, f.Name as K, actionName); + const control: { new(...args: any[]): TControlBaseType } = this.GetControlForContentField(contentType, f.Name as K, actionName); return { FieldSettings: f, ClientSettings: clientSetting, @@ -208,7 +206,7 @@ export class ControlMapper { Schema: schema, ContentTypeControl: this.GetControlForContentType(contentType), FieldMappings: mappings - } + }; } /** @@ -217,7 +215,7 @@ export class ControlMapper { * @param actionName The name of the Action (can be 'new' / 'edit' / 'view') * @returns the fully created ControlSchema */ - public GetFullSchemaForContent(content: TContentType, actionName: ActionName){ - return this.GetFullSchemaForContentType(content.constructor as {new(...args: any[])}, actionName); + public GetFullSchemaForContent(content: TContentType, actionName: ActionName) { + return this.GetFullSchemaForContentType(content.constructor as { new(...args: any[]): TContentType }, actionName); } -} \ No newline at end of file +} diff --git a/src/Enums.ts b/src/Enums.ts index ee7c631..7200b9b 100644 --- a/src/Enums.ts +++ b/src/Enums.ts @@ -18,6 +18,8 @@ * ``` */ /** */ + // tslint:disable:naming-convention + export enum VersioningMode { Option0 = '0', Option1 = '1', diff --git a/src/FieldSettings.ts b/src/FieldSettings.ts index dd40328..8b2f8cd 100644 --- a/src/FieldSettings.ts +++ b/src/FieldSettings.ts @@ -17,322 +17,143 @@ import { ComplexTypes } from './SN'; /** * Enum for Field visibility values. */ - export enum FieldVisibility { Show, Hide, Advanced } +export enum FieldVisibility { Show, Hide, Advanced } /** * Enum for Field output method values. */ - export enum OutputMethod { Default, Raw, Text, Html } +export enum OutputMethod { Default, Raw, Text, Html } /** * Enum for Choice Field control values. */ - export enum DisplayChoice { DropDown, RadioButtons, CheckBoxes } +export enum DisplayChoice { DropDown, RadioButtons, CheckBoxes } /** * Enum for DateTime Field mode values. */ - export enum DateTimeMode { None, Date, DateAndTime } +export enum DateTimeMode { None, Date, DateAndTime } /** * Enum for DateTime Field precision values. */ - export enum DateTimePrecision { Millisecond, Second, Minute, Hour, Day } +export enum DateTimePrecision { Millisecond, Second, Minute, Hour, Day } /** * Enum for LongText field editor values. */ - export enum TextType { LongText, RichText, AdvancedRichText } +export enum TextType { LongText, RichText, AdvancedRichText } /** * Enum for HyperLink field href values. */ - export enum UrlFormat { Hyperlink, Picture } +export enum UrlFormat { Hyperlink, Picture } + +// tslint:disable-next-line:only-arrow-functions +export function isFieldSettingOfType(setting: FieldSetting, type: {new(): T}): setting is T { + return setting.Type === type.name; +} + +export class FieldSetting { + public Name: string; + public Type: string; + public DisplayName?: string; + public Description?: string; + public Icon?: string; + public ReadOnly?: boolean; + public Compulsory?: boolean; + public DefaultValue?: string; + public OutputMethod?: OutputMethod; + public VisibleBrowse?: FieldVisibility; + public VisibleNew?: FieldVisibility; + public VisibleEdit?: FieldVisibility; + public FieldIndex?: number; + public DefaultOrder?: number; + public ControlHint?: string; - export class FieldSetting { - Name: string; - DisplayName?: string; - Description?: string; - Icon?: string; - ReadOnly?: boolean; - Compulsory?: boolean; - DefaultValue?: string; - OutputMethod?: OutputMethod; - VisibleBrowse?: FieldVisibility; - VisibleNew?: FieldVisibility; - VisibleEdit?: FieldVisibility; - FieldIndex?: number; - DefaultOrder?: number; - ControlHint?: string; - - constructor(options: IFieldSettingOptions) { - this.Name = options.name || 'Content'; - this.DisplayName = options.displayName; - this.Icon = options.icon; - this.ReadOnly = options.readOnly; - this.Compulsory = options.compulsory; - this.DefaultValue = options.defaultValue; - this.OutputMethod = options.outputMethod; - this.VisibleBrowse = options.visibleBrowse; - this.VisibleEdit = options.visibleEdit; - this.VisibleNew = options.visibleNew; - this.FieldIndex = options.fieldIndex; - this.DefaultOrder = options.defaultOrder; - this.ControlHint = options.controlHint; - - } } - export interface IFieldSettingOptions { - name?: string; - displayName?: string; - description?: string; - icon?: string; - readOnly?: boolean; - compulsory?: boolean; - defaultValue?: string; - outputMethod?: OutputMethod; - visibleBrowse?: FieldVisibility; - visibleNew?: FieldVisibility; - visibleEdit?: FieldVisibility; - fieldIndex?: number; - defaultOrder?: number; - controlHint?: string; - } - - // Used in ContentType, GenericContent, File, Image, TrashBag, TrashBin, Task - export class IntegerFieldSetting extends FieldSetting { - MinValue?: number; - MaxValue?: number; - ShowAsPercentage?: boolean; - Step?: number; - - constructor(options: IIntegerFieldSettingOptions) { - super(options); - this.MinValue = options.minValue; - this.MaxValue = options.maxValue; - this.ShowAsPercentage = options.showAsPercentage; - this.Step = options.step; - } - } - - export interface IIntegerFieldSettingOptions extends IFieldSettingOptions { - minValue?: number; - maxValue?: number; - showAsPercentage?: boolean; - step?: number; +export class IntegerFieldSetting extends FieldSetting { + public MinValue?: number; + public MaxValue?: number; + public ShowAsPercentage?: boolean; + public Step?: number; } // - export class TextFieldSetting extends FieldSetting { - MinLength?: number; - MaxLength?: number; - - constructor(options: ITextFieldSettingOptions) { - super(options); - this.MinLength = options.minLength; - this.MaxLength = options.maxLength; - } - } - - export interface ITextFieldSettingOptions extends IFieldSettingOptions { - minLength?: number; - maxLength?: number; +export class TextFieldSetting extends FieldSetting { + public MinLength?: number; + public MaxLength?: number; } // Used in ContentType, GenericContent, File, ContentList, Device, Domain, Email, OrganizationalUnit, TrashBag, Group, Task, User - export class ShortTextFieldSetting extends TextFieldSetting { - Regex?: string; - - constructor(options: IShortTextFieldSettingOptions) { - super(options); - this.Regex = options.regex; - } - } - - export interface IShortTextFieldSettingOptions extends ITextFieldSettingOptions { - regex?: string; +export class ShortTextFieldSetting extends TextFieldSetting { + public Regex?: string; } // Used in ContentType, GenericContent, Settings, IndexingSettings, ContentList, Workspace, Site, CustomListItem, User - export class NullFieldSetting extends FieldSetting { - - constructor(options: INullFieldSettingOptions) { - super(options); - } - } - - export interface INullFieldSettingOptions extends IFieldSettingOptions { +export class NullFieldSetting extends FieldSetting { } // Used in ContentType, GenericContent, File, HtmlTemplate, Image, ContentList, Aspect, Email, SmartFolder, Query, User - export class LongTextFieldSetting extends TextFieldSetting { - Rows?: number; - TextType?: TextType; - AppendModifications?: boolean; - - constructor(options: ILongTextFieldSettingOptions) { - super(options); - this.Rows = options.rows; - this.TextType = options.textType; - this.AppendModifications = options.appendModifications; - } - } - - export interface ILongTextFieldSettingOptions extends ITextFieldSettingOptions { - rows?: number; - textType?: TextType; - appendModifications?: boolean; +export class LongTextFieldSetting extends TextFieldSetting { + public Rows?: number; + public TextType?: TextType; + public AppendModifications?: boolean; } // Used in ContentType, File, User - export class BinaryFieldSetting extends FieldSetting { - IsText?: boolean; - - constructor(options: IBinaryFieldSettingOptions) { - super(options); - this.IsText = options.isText; - } - } - - export interface IBinaryFieldSettingOptions extends IFieldSettingOptions { - isText?: boolean; +export class BinaryFieldSetting extends FieldSetting { + public IsText?: boolean; } // Used in ContentType, GenericContent, ContentLink, ContentList, ImageLibrary, TrashBag, Workspace, Site, UserProfile, Group, Memo, Task, User - export class ReferenceFieldSetting extends FieldSetting { - AllowMultiple?: boolean; - AllowedTypes?: string[]; - SelectionRoots?: string[]; - Query?: string; - FieldName?: string; - - constructor(options: IReferenceFieldSettingOptions) { - super(options); - this.AllowMultiple = options.allowMultiple; - this.AllowedTypes = options.allowedTypes; - this.SelectionRoots = options.selectionRoots; - this.Query = options.query; - this.FieldName = options.fieldName; - } - } +export class ReferenceFieldSetting extends FieldSetting { + public AllowMultiple?: boolean; + public AllowedTypes?: string[]; + public SelectionRoots?: string[]; + public Query?: string; + public FieldName?: string; - export interface IReferenceFieldSettingOptions extends IFieldSettingOptions { - allowMultiple?: boolean; - allowedTypes?: string[]; - selectionRoots?: string[]; - query?: string; - fieldName?: string; } // Used in ContentType, GenericContent, Image, Domain, Email, OrganizationalUnit, TrashBag, Workspace, Group, Memo, Task, User - export class DateTimeFieldSetting extends FieldSetting { - DateTimeMode?: DateTimeMode; - Precision?: DateTimePrecision; - - constructor(options: IDateTimeFieldSettingOptions) { - super(options); - this.DateTimeMode = options.dateTimeMode; - this.Precision = options.precision; - } - } - - export interface IDateTimeFieldSettingOptions extends IFieldSettingOptions { - dateTimeMode?: DateTimeMode; - precision?: DateTimePrecision; +export class DateTimeFieldSetting extends FieldSetting { + public DateTimeMode?: DateTimeMode; + public Precision?: DateTimePrecision; } // Used in GenericContent, ContentList, SmartFolder, Site, Memo, Task, Query, User - export class ChoiceFieldSetting extends ShortTextFieldSetting { - AllowExtraValue?: boolean; - AllowMultiple?: boolean; - Options?: ComplexTypes.ChoiceOption[]; - DisplayChoice?: DisplayChoice; - EnumTypeName?: string; +export class ChoiceFieldSetting extends ShortTextFieldSetting { + public AllowExtraValue?: boolean; + public AllowMultiple?: boolean; + public Options?: ComplexTypes.ChoiceOption[]; + public DisplayChoice?: DisplayChoice; + public EnumTypeName?: string; - constructor(options: IChoiceFieldSettingOptions) { - super(options); - this.AllowExtraValue = options.allowExtraValue; - this.AllowMultiple = options.allowMultiple; - this.Options = options.options; - this.DisplayChoice = options.displayChoice; - this.EnumTypeName = options.enumTypeName; - } - } - - export interface IChoiceFieldSettingOptions extends IShortTextFieldSettingOptions { - allowExtraValue?: boolean; - allowMultiple?: boolean; - options?: ComplexTypes.ChoiceOption[]; - displayChoice?: DisplayChoice; - enumTypeName?: string; } // Used in GenericContent, File, Resource - export class NumberFieldSetting extends FieldSetting { - MinValue?: number; - MaxValue?: number; - Digits?: number; - ShowAsPercentage?: boolean; - Step?: number; - - constructor(options: INumberFieldSettingOptions) { - super(options); - this.MinValue = options.minValue; - this.MaxValue = options.maxValue; - this.Digits = options.digits; - this.ShowAsPercentage = options.showAsPercentage; - this.Step = options.step; - } - } +export class NumberFieldSetting extends FieldSetting { + public MinValue?: number; + public MaxValue?: number; + public Digits?: number; + public ShowAsPercentage?: boolean; + public Step?: number; - export interface INumberFieldSettingOptions extends IFieldSettingOptions { - minValue?: number; - maxValue?: number; - digits?: number; - showAsPercentage?: boolean; - step?: number; } // Used in GenericContent - export class RatingFieldSetting extends ShortTextFieldSetting { - Range?: number; - Split?: number; - - constructor(options: IRatingFieldSettingOptions) { - super(options); - this.Range = options.range; - this.Split = options.split; - } - } +export class RatingFieldSetting extends ShortTextFieldSetting { + public Range?: number; + public Split?: number; - export interface IRatingFieldSettingOptions extends IShortTextFieldSettingOptions { - range?: number; - split?: number; } // Used in User - export class PasswordFieldSetting extends ShortTextFieldSetting { - ReenterTitle?: string; - ReenterDescription?: string; - PasswordHistoryLength?: number; +export class PasswordFieldSetting extends ShortTextFieldSetting { + public ReenterTitle?: string; + public ReenterDescription?: string; + public PasswordHistoryLength?: number; - constructor(options: IPasswordFieldSettingOptions) { - super(options); - this.ReenterTitle = options.reenterTitle; - this.ReenterDescription = options.reenterDescription; - this.PasswordHistoryLength = options.passwordHistoryLength; - } - } - - export interface IPasswordFieldSettingOptions extends IShortTextFieldSettingOptions { - reenterTitle?: string; - reenterDescription?: string; - passwordHistoryLength?: number; } // Used in User - export class CaptchaFieldSetting extends FieldSetting { - - constructor(options: ICaptchaFieldSettingOptions) { - super(options); - } - } +export class CaptchaFieldSetting extends FieldSetting { - export interface ICaptchaFieldSettingOptions extends IFieldSettingOptions { } diff --git a/src/HttpProviders/BaseHttpProvider.ts b/src/HttpProviders/BaseHttpProvider.ts index 3b54cb2..296b994 100644 --- a/src/HttpProviders/BaseHttpProvider.ts +++ b/src/HttpProviders/BaseHttpProvider.ts @@ -2,7 +2,8 @@ * @module HttpProviders *//** */ -import { Observable, AjaxRequest } from '@reactivex/rxjs'; +import { Observable } from 'rxjs/Observable'; +import { AjaxRequest } from 'rxjs/observable/dom/AjaxObservable'; /** * */ @@ -24,7 +25,7 @@ export abstract class BaseHttpProvider { * */ public UnsetGlobalHeader(headerName: string) { - if (this._headers[headerName as any]){ + if (this._headers[headerName as any]) { delete this._headers[headerName as any]; } } @@ -35,18 +36,20 @@ export abstract class BaseHttpProvider { * @param options Additional RxJs AjaxRequest options (the global headers will be overridden) */ public Ajax(tReturnType: { new (...args: any[]): T }, options: AjaxRequest, additionalHeaders: {name: string, value: string}[] = []): Observable { - options.headers = options.headers || []; - for (let key in this._headers){ - options.headers[key] = this._headers[key]; + const headers = options.headers || []; + // tslint:disable-next-line:forin + for (const key in this._headers) { + headers[key] = this._headers[key]; } - additionalHeaders.forEach(h => { - if (options.headers) - options.headers[h.name] = h.value; - }) + additionalHeaders.forEach((h) => { + headers[h.name] = h.value; + }); + + options.headers = headers; return this.ajaxInner(tReturnType, options); - }; + } /** * Public entry point for uploading files using a specific provider @@ -57,7 +60,7 @@ export abstract class BaseHttpProvider { options.headers = options.headers || []; return this.uploadInner(tReturnType, File, options); - }; + } /** * The inner implementation of the Ajax call @@ -71,5 +74,5 @@ export abstract class BaseHttpProvider { * @param tReturnType The return type * @param options Additional RxJs AjaxRequest options (the global headers will be overridden) */ - protected abstract uploadInner(returnType: {new(...args: any[]): T}, File: File, options?: AjaxRequest & {url: string}): Observable -} \ No newline at end of file + protected abstract uploadInner(returnType: {new(...args: any[]): T}, File: File, options?: AjaxRequest & {url: string}): Observable; +} diff --git a/src/HttpProviders/RxAjaxHttpProvider.ts b/src/HttpProviders/RxAjaxHttpProvider.ts index b0a3033..51c4237 100644 --- a/src/HttpProviders/RxAjaxHttpProvider.ts +++ b/src/HttpProviders/RxAjaxHttpProvider.ts @@ -2,33 +2,34 @@ * @module HttpProviders *//** */ -import { Observable, AjaxRequest, Subject } from '@reactivex/rxjs'; -import { BaseHttpProvider } from './'; +import { Observable } from 'rxjs/Observable'; +import { AjaxRequest } from 'rxjs/observable/dom/AjaxObservable'; +import { Subject } from 'rxjs/Subject'; + import { SnConfigModel } from '../Config'; +import { BaseHttpProvider } from './'; + +import 'rxjs/add/observable/dom/ajax'; /** * This is the default RxJs-Ajax based Http calls. */ export class RxAjaxHttpProvider extends BaseHttpProvider { - protected uploadInner(returnType: new (...args: any[]) => T, File: File, options: AjaxRequest & { url: string }): Observable { + protected uploadInner(returnType: new (...args: any[]) => T, File: File, options: AjaxRequest & { url: string, headers: string[], body: any}): Observable { const subject = new Subject(); const formData = new FormData(); - formData.append(File.name || 'File', File); + formData.append(File.name, File); - if (options.body) { - for (const index in options.body) { - formData.append(index, options.body[index]); - } + for (const index in options.body) { + formData.append(index, options.body[index]); } const request = new XMLHttpRequest(); request.withCredentials = this.isCrossDomain(options.url); request.open('POST', options.url); - if (options.headers) { - for (const header in options.headers) { - request.setRequestHeader(header, options.headers[header]); - } + for (const header in options.headers) { + request.setRequestHeader(header, (options.headers as any)[header]); } request.onreadystatechange = () => { @@ -44,10 +45,10 @@ export class RxAjaxHttpProvider extends BaseHttpProvider { } break; default: - subject.error({ message: 'Invalid Request status', request }) + subject.error({ message: 'Invalid Request status', request }); } } - } + }; request.send(formData); return subject.asObservable(); } @@ -62,11 +63,11 @@ export class RxAjaxHttpProvider extends BaseHttpProvider { this.SetGlobalHeader('Accept', 'application/json'); } - protected ajaxInner(tReturnType, options: AjaxRequest): Observable { + protected ajaxInner(tReturnType: {new(...args: any[]): T}, options: AjaxRequest): Observable { const crossDomain = this.isCrossDomain(options.url || ''); options.withCredentials = crossDomain; options.crossDomain = crossDomain; - return Observable.ajax(options).map(req => req.response as T).share(); + return Observable.ajax(options).map((req) => req.response as T).share(); } -} \ No newline at end of file +} diff --git a/src/HttpProviders/index.ts b/src/HttpProviders/index.ts index 839ec0c..fefbbd2 100644 --- a/src/HttpProviders/index.ts +++ b/src/HttpProviders/index.ts @@ -4,4 +4,4 @@ * @description Library module for storing HttpProvider abstracts and implementations. *//** */ export * from './BaseHttpProvider'; -export * from './RxAjaxHttpProvider'; \ No newline at end of file +export * from './RxAjaxHttpProvider'; diff --git a/src/ODataApi/CustomAction.ts b/src/ODataApi/CustomAction.ts index 49373d1..053d6a8 100644 --- a/src/ODataApi/CustomAction.ts +++ b/src/ODataApi/CustomAction.ts @@ -2,17 +2,19 @@ * @module ODataApi */ /** */ + // tslint:disable:naming-convention + /** * Class that represents a custom OData Action */ export class CustomAction { - name: string; - id?: number; - path?: string; - params: string[] = []; - requiredParams: string[] = []; - isAction: boolean = false; - noCache: boolean = false; + public name: string; + public id?: number; + public path?: string; + public params: string[] = []; + public requiredParams: string[] = []; + public isAction: boolean = false; + public noCache: boolean = false; constructor(options: ICustomActionOptions) { this.name = options.name; this.id = options.id; @@ -20,14 +22,10 @@ export class CustomAction { this.isAction = options.isAction || false; this.noCache = options.noCache || false; if (options.params) { - for (let i = 0; i < options.params.length; i++) { - this.params.push(options.params[i]); - } + this.params = this.params.concat(options.params); } if (options.requiredParams) { - for (let i = 0; i < options.requiredParams.length; i++) { - this.params.push(options.requiredParams[i]); - } + this.params = this.params.concat(options.requiredParams); } } } @@ -43,4 +41,4 @@ export interface ICustomActionOptions { requiredParams?: string[]; isAction?: boolean; noCache?: boolean; -} \ No newline at end of file +} diff --git a/src/ODataApi/ODataApi.ts b/src/ODataApi/ODataApi.ts index e902226..5129799 100644 --- a/src/ODataApi/ODataApi.ts +++ b/src/ODataApi/ODataApi.ts @@ -2,12 +2,12 @@ * @module ODataApi */ /** */ +import { Observable } from 'rxjs/Observable'; +import { IContent, ISavedContent, SavedContent } from '../Content'; import { BaseHttpProvider } from '../HttpProviders'; -import { IODataParams, CustomAction, ODataResponse, ICustomActionOptions, ODataCollectionResponse, IODataRequestOptions } from './'; -import { ODataHelper } from '../SN'; -import { Observable } from '@reactivex/rxjs'; import { BaseRepository } from '../Repository/BaseRepository'; -import { Content } from '../Content'; +import { ODataHelper } from '../SN'; +import { CustomAction, ICustomActionOptions, IODataParams, IODataRequestOptions, ODataCollectionResponse, ODataResponse } from './'; /** * This class contains methods and classes for sending requests and getting responses from the Content Repository through OData REST API. @@ -15,22 +15,18 @@ import { Content } from '../Content'; * Following methods return Rxjs Observables which are made from the ajax requests' promises. Action methods like Delete or Rename on Content calls this methods, * gets their responses as Observables and returns them so that you can subscribe them in your code. */ -export class ODataApi{ +export class ODataApi { /** * The HTTP provider instance for making AJAX calls. */ - private readonly _httpProvider: THttpProvider; /** - * @param {THttpProvider} providerRef Reference to a specifed HTTP Provider to make Ajax calls * @param {BaseRepository} repository Reference to a Repository instance */ constructor( - providerRef: { new(): THttpProvider }, private readonly _repository: BaseRepository, ) { - this._httpProvider = new providerRef(); } /** @@ -49,9 +45,9 @@ export class ODataApi{ * }); * ``` */ - public Get(options: IODataRequestOptions, returns?: { new(...args: any[]): T }): Observable> { + public Get(options: IODataRequestOptions): Observable> { - return this._repository.Ajax>(`${options.path}?${ODataHelper.buildUrlParamString(this._repository.Config, options.params)}`, 'GET').share(); + return this._repository.Ajax>(`${options.path}?${ODataHelper.buildUrlParamString(this._repository.Config, options.params)}`, 'GET').share(); } /** @@ -71,11 +67,9 @@ export class ODataApi{ * }); * ``` */ - public Fetch( - options: IODataRequestOptions, - returnsType?: { new(...args: any[]): T['options'] }): Observable> { + public Fetch(options: IODataRequestOptions): Observable> { - return this._repository.Ajax>(`${options.path}?${ODataHelper.buildUrlParamString(this._repository.Config, options.params)}`, 'GET').share(); + return this._repository.Ajax>(`${options.path}?${ODataHelper.buildUrlParamString(this._repository.Config, options.params)}`, 'GET').share(); } /** @@ -98,15 +92,13 @@ export class ODataApi{ * }); * ``` */ - public Post( + public Post( path: string, - contentBody: T['options'], - postedContentType: { new(...args: any[]): T }): Observable { - - (contentBody as any).__ContentType = contentBody.Type || postedContentType.name; + contentBody: T): Observable> { + (contentBody as T & {'__ContentType': string | undefined}).__ContentType = contentBody.Type; return this._repository .Ajax(ODataHelper.getContentURLbyPath(path), 'POST', ODataResponse, JSON.stringify(contentBody)) - .map(resp => resp.d) + .map((resp) => resp.d) .share(); } @@ -118,8 +110,7 @@ export class ODataApi{ * @returns {Observable} Returns an observable that you can subscribe of in your code. */ public Delete = (id: number, permanent?: boolean): Observable => - this._repository.Ajax(`/content(${id})`, 'DELETE', Object, { 'permanent': permanent }).share(); - + this._repository.Ajax(`/content(${id})`, 'DELETE', Object, { permanent }).share() /** * Method to modify a single or multiple fields of a Content through OData REST API. @@ -138,14 +129,13 @@ export class ODataApi{ * }); * ``` */ - public Patch(id: number, contentType: { new(...args: any[]): T }, fields: T['options']): Observable { + public Patch(id: number, fields: T): Observable { - let contentTypeWithResponse = ODataResponse as { new(...args: any[]): ODataResponse }; + const contentTypeWithResponse = ODataResponse as { new(...args: any[]): ODataResponse }; return this._repository.Ajax(`/content(${id})`, 'PATCH', contentTypeWithResponse, `models=[${JSON.stringify(fields)}]`) - .map(result => result.d); + .map((result) => result.d); } - /** * Method to set multiple fields of a Content and clear the rest through OData REST API. * @@ -164,45 +154,43 @@ export class ODataApi{ * }); * ``` */ - public Put(id: number, contentType: { new(...args: any[]): T }, fields: T['options']): Observable { - let contentTypeWithResponse = ODataResponse as { new(...args: any[]): ODataResponse }; + public Put(id: number, fields: T): Observable> { + const contentTypeWithResponse = ODataResponse as { new(...args: any[]): ODataResponse> }; return this._repository.Ajax(`/content(${id})`, 'PUT', contentTypeWithResponse, `models=[${JSON.stringify(fields)}]`) - .map(result => result.d); + .map((result) => result.d); } /** - * Creates a wrapper function for a callable custom OData action. - * - * This method creates an Observable, sends an ajax request to the server and convert the reponse to promise which will be the argument of the Observable. - * @param {ICustomActionOptions} actionOptions A CustomAction configuration object. - * @param {IODataParams} options An object that holds the config of the ajax request like urlparameters or data. - * @param {new(...args): TReturnType} returns Th type that the action should return - * @returns {Observable} Returns an Rxjs observable whitch will be resolved with TReturnType that you can subscribe of in your code. - */ + * Creates a wrapper function for a callable custom OData action. + * + * This method creates an Observable, sends an ajax request to the server and convert the reponse to promise which will be the argument of the Observable. + * @param {ICustomActionOptions} actionOptions A CustomAction configuration object. + * @param {IODataParams} options An object that holds the config of the ajax request like urlparameters or data. + * @param {new(...args): TReturnType} returns Th type that the action should return + * @returns {Observable} Returns an Rxjs observable whitch will be resolved with TReturnType that you can subscribe of in your code. + */ public CreateCustomAction(actionOptions: ICustomActionOptions, options?: IODataParams, returns?: { new(...args: any[]): TReturnType }): Observable { - if (!returns) { - returns = Object as { new(...args: any[]): any }; - } - let action = new CustomAction(actionOptions); - let cacheParam = (action.noCache) ? '' : '&nocache=' + new Date().getTime(); + + const returnsType = returns || Object as { new(...args: any[]): any }; + const action = new CustomAction(actionOptions); + const cacheParam = (action.noCache) ? '' : '&nocache=' + new Date().getTime(); let path = ''; if (typeof action.id !== 'undefined') { path = ODataHelper.joinPaths(ODataHelper.getContentUrlbyId(action.id), action.name); - } - else if (action.path) { + } else if (action.path) { path = ODataHelper.joinPaths(ODataHelper.getContentURLbyPath(action.path), action.name); } else { - const error = new Error('No Id or Path provided.') + const error = new Error('No Id or Path provided.'); this._repository.Events.Trigger.CustomActionFailed({ ActionOptions: actionOptions, ODataParams: options, - ResultType: returns, + ResultType: returnsType, Error: error }); throw error; } if (cacheParam.length > 0) { - path = `${path}?${cacheParam}` + path = `${path}?${cacheParam}`; } if (path.indexOf('OData.svc(') > -1) { @@ -211,8 +199,8 @@ export class ODataApi{ } if (typeof action.isAction === 'undefined' || !action.isAction) { - const ajax = this._repository.Ajax(path, 'GET', returns).share(); - ajax.subscribe(resp => { + const ajax = this._repository.Ajax(path, 'GET', returnsType).share(); + ajax.subscribe((resp) => { this._repository.Events.Trigger.CustomActionExecuted({ ActionOptions: actionOptions, ODataParams: options, @@ -222,16 +210,15 @@ export class ODataApi{ this._repository.Events.Trigger.CustomActionFailed({ ActionOptions: actionOptions, ODataParams: options, - ResultType: returns as any, + ResultType: returnsType, Error: err }); }); return ajax; - } - else { + } else { if (typeof options !== 'undefined' && typeof options.data !== 'undefined') { - const ajax = this._repository.Ajax(path, 'POST', returns, JSON.stringify(options.data)).share(); - ajax.subscribe(resp => { + const ajax = this._repository.Ajax(path, 'POST', returnsType, JSON.stringify(options.data)).share(); + ajax.subscribe((resp) => { this._repository.Events.Trigger.CustomActionExecuted({ ActionOptions: actionOptions, ODataParams: options, @@ -241,15 +228,14 @@ export class ODataApi{ this._repository.Events.Trigger.CustomActionFailed({ ActionOptions: actionOptions, ODataParams: options, - ResultType: returns as any, + ResultType: returnsType, Error: err }); }); return ajax; - } - else { - const ajax = this._repository.Ajax(path, 'POST', returns).share(); - ajax.subscribe(resp => { + } else { + const ajax = this._repository.Ajax(path, 'POST', returnsType).share(); + ajax.subscribe((resp) => { this._repository.Events.Trigger.CustomActionExecuted({ ActionOptions: actionOptions, ODataParams: options, @@ -259,7 +245,7 @@ export class ODataApi{ this._repository.Events.Trigger.CustomActionFailed({ ActionOptions: actionOptions, ODataParams: options, - ResultType: returns as any, + ResultType: returnsType, Error: err }); }); @@ -268,14 +254,13 @@ export class ODataApi{ } } - public Upload = (path: string, data: Object, creation: boolean): Observable => { + public Upload = (path: string, data: any, creation: boolean): Observable => { let url = `${ODataHelper.getContentURLbyPath(path)}/Upload`; if (creation) { url = `${url}?create=1`; - } - else { + } else { url = url; } return this._repository.Ajax(url, 'POST', Object, data); } -} \ No newline at end of file +} diff --git a/src/ODataApi/ODataBatchResponse.ts b/src/ODataApi/ODataBatchResponse.ts new file mode 100644 index 0000000..70f07be --- /dev/null +++ b/src/ODataApi/ODataBatchResponse.ts @@ -0,0 +1,13 @@ +import { ISavedContent } from '../Content'; + +// tslint:disable:naming-convention +export class ODataBatchResponse { + + public d: { + __count: number, + results: T[], + + errors: { content: T, error: any }[], + }; + +} diff --git a/src/ODataApi/ODataCollectionResponse.ts b/src/ODataApi/ODataCollectionResponse.ts index 64eb067..d13915f 100644 --- a/src/ODataApi/ODataCollectionResponse.ts +++ b/src/ODataApi/ODataCollectionResponse.ts @@ -5,11 +5,12 @@ /** * Generic Class that represents a basic OData Response structure */ -import { IContentOptions } from '../Content'; +import { IContent, ISavedContent } from '../Content'; -export class ODataCollectionResponse{ - d: { - results: T[]; +export class ODataCollectionResponse { + // tslint:disable-next-line:naming-convention + public d: { + results: (T & ISavedContent)[]; __count: number; - } -} \ No newline at end of file + }; +} diff --git a/src/ODataApi/ODataParams.ts b/src/ODataApi/ODataParams.ts index 71a5441..2a23c93 100644 --- a/src/ODataApi/ODataParams.ts +++ b/src/ODataApi/ODataParams.ts @@ -2,10 +2,12 @@ * @module ODataApi */ /** */ -import { Content } from '../Content'; + // tslint:disable:naming-convention -export type ODataFieldParameter = (keyof T['options'])[] | keyof T['options']; -export type ODataOrderParameter = keyof T['options'] | (keyof T['options'] | [keyof T['options'], 'asc' | 'desc'])[]; +import { IContent } from '../Content'; + +export type ODataFieldParameter = (keyof T)[] | keyof T; +export type ODataOrderParameter = keyof T | (keyof T | [keyof T, 'asc' | 'desc'])[]; export type ODataMetadataType = 'full' | 'minimal' | 'no'; export type ODataFormatType = 'json' | 'verbosejson'; @@ -14,15 +16,15 @@ export type ODataInlineCountType = 'none' | 'allpages'; /** * Model class to define specific OData Request parameters. See http://wiki.sensenet.com/OData_REST_API */ -export class IODataParams { +export class IODataParams { /** * The field(s) to be include in a $select list. Can be a field (e.g. 'DisplayName'), an array of fields (e.g. ['Name', 'Type']) or 'all' */ - select?: ODataFieldParameter | 'all'; + public select?: ODataFieldParameter | 'all'; /** * The field(s) to be include in an $expand list. Can be a reference field (e.g. 'Owner') or an array of fields (e.g. ['CreatedBy', 'ModifiecBy']) */ - expand?: ODataFieldParameter; + public expand?: ODataFieldParameter; /** * Sets the OData $orderby parameter. Usage example @@ -45,46 +47,46 @@ export class IODataParams { * * ``` */ - orderby?: ODataOrderParameter; + public orderby?: ODataOrderParameter; /** * Sets the OData $top parameter */ - top?: number; + public top?: number; /** * Sets the OData $skip parameter */ - skip?: number; + public skip?: number; /** * Sets the OData $filter parameter */ - filter?: string; + public filter?: string; /** * Sets the OData $format parameter. Can be 'json' or 'verbosejson' */ - format?: ODataFormatType; + public format?: ODataFormatType; /** * Sets the OData $format parameter. Can be 'json' or 'verbosejson' */ - inlinecount?: ODataInlineCountType; + public inlinecount?: ODataInlineCountType; /** * Sets the OData 'query' parameter. Can be a Content Query */ - query?: string; + public query?: string; /** * Sets the OData metadata parameter. Can be 'full', 'minimal' or 'no' */ - metadata?: ODataMetadataType; + public metadata?: ODataMetadataType; /** * Sets the OData post data object */ - data?: Object; + public data?: any; /** * Sets the OData Scenario parameter */ - scenario?: string; -} \ No newline at end of file + public scenario?: string; +} diff --git a/src/ODataApi/ODataRequestOptions.ts b/src/ODataApi/ODataRequestOptions.ts index 000f7cc..d8c2e5d 100644 --- a/src/ODataApi/ODataRequestOptions.ts +++ b/src/ODataApi/ODataRequestOptions.ts @@ -2,16 +2,17 @@ * @module ODataApi */ /** */ +import { IContent } from '../Content'; import { IODataParams } from './'; -import { Content } from '../Content'; +// tslint:disable:naming-convention -export class IODataRequestOptions { - path: string; - params?: IODataParams; - async?: boolean; - type?: string; - success?: Function; - error?: Function; - complete?: Function; -} \ No newline at end of file +export class IODataRequestOptions { + public path: string; + public params?: IODataParams; + public async?: boolean; + public type?: string; + public success?: () => void; + public error?: () => void; + public complete?: () => void; +} diff --git a/src/ODataApi/ODataResponse.ts b/src/ODataApi/ODataResponse.ts index 0a4fd89..53dce1d 100644 --- a/src/ODataApi/ODataResponse.ts +++ b/src/ODataApi/ODataResponse.ts @@ -5,8 +5,9 @@ /** * Generic Class that represents a basic OData Response structure */ -import { IContentOptions } from '../Content'; +import { ISavedContent } from '../Content'; -export class ODataResponse{ - d: T; -} \ No newline at end of file +export class ODataResponse { + // tslint:disable-next-line:naming-convention + public d: T; +} diff --git a/src/ODataApi/index.ts b/src/ODataApi/index.ts index 4457c63..7b35c06 100644 --- a/src/ODataApi/index.ts +++ b/src/ODataApi/index.ts @@ -6,7 +6,8 @@ */ /** */ export * from './CustomAction'; export * from './ODataApi'; +export * from './ODataBatchResponse'; export * from './ODataParams'; export * from './ODataRequestOptions'; export * from './ODataResponse'; -export * from './ODataCollectionResponse'; \ No newline at end of file +export * from './ODataCollectionResponse'; diff --git a/src/ODataHelper.ts b/src/ODataHelper.ts index e020e1c..260a5be 100644 --- a/src/ODataHelper.ts +++ b/src/ODataHelper.ts @@ -6,24 +6,24 @@ */ /** */ // TODO: ezeket vhova kivezetni -import { IODataParams, ODataFieldParameter } from './ODataApi'; import { SnConfigModel } from './Config/snconfigmodel'; -import { Content } from './Content'; +import { IContent } from './Content'; +import { IODataParams, ODataFieldParameter } from './ODataApi'; +import { Content } from './SN'; const ODATA_PARAMS = ['select', 'expand', 'orderby', 'top', 'skip', 'filter', 'format', 'inlinecount']; export const DATA_ROOT = 'OData.svc'; - -export const combineODataFieldParameters: (...params: ODataFieldParameter[]) => ODataFieldParameter - = (...params: ODataFieldParameter[]) => { - params.forEach(param => { +export const combineODataFieldParameters: (...params: ODataFieldParameter[]) => ODataFieldParameter> + = (...params: ODataFieldParameter[]) => { + params.forEach((param) => { if (typeof param === 'string') { param = [param]; } }); - params = params.filter(param => param && param.length > 0); + params = params.filter((param) => param && param.length > 0); return [...new Set([].concat.apply([], params))] as ODataFieldParameter; - } + }; /** * Method to build proper parameter string to OData requests based on the given option Object. @@ -34,35 +34,38 @@ export const combineODataFieldParameters: (...params: ODataFi * @param {IODataOptions} options Represents an ODataOptions obejct based through the IODataOptions interface. Holds the possible url parameters as properties. * @returns {string} String with the url params in the correct format e.g. '$select=DisplayName,Index'&$top=2&metadata=no'. */ -export const buildUrlParamString: (config: Partial, options?: IODataParams) => string = (config: SnConfigModel, options?: IODataParams): string => { - if (!options) { - return ''; - } - if (config.RequiredSelect === 'all' || config.DefaultSelect === 'all' || options.select === 'all') { - options.select = undefined; - } else { - options.select = combineODataFieldParameters(config.RequiredSelect, options.select || config.DefaultSelect) - } - options.metadata = options.metadata || config.DefaultMetadata; - options.inlinecount = options.inlinecount || config.DefaultInlineCount; - options.expand = options.expand || config.DefaultExpand; - options.top = options.top || config.DefaultTop; +export const buildUrlParamString: (config: SnConfigModel, options?: IODataParams) => string = + (config: SnConfigModel, options?: IODataParams): string => { + if (!options) { + return ''; + } - const segments: {name: string, value: string}[] = []; - for (let key in options) { - const name = ODATA_PARAMS.indexOf(key) > -1 ? `$${key}` : key; - const plainValue = options[key]; - let parsedValue = plainValue; - if (plainValue instanceof Array && plainValue.length && plainValue.length > 0){ - parsedValue = plainValue.map(v => v.join && v.join(' ') || v).join(','); + if (config.RequiredSelect === 'all' || config.DefaultSelect === 'all' || options.select === 'all') { + options.select = undefined; + } else { + options.select = combineODataFieldParameters(config.RequiredSelect, options.select || config.DefaultSelect) as any; } - if (name && parsedValue && parsedValue.length){ - segments.push({name, value: parsedValue}); + options.metadata = options.metadata || config.DefaultMetadata; + options.inlinecount = options.inlinecount || config.DefaultInlineCount; + options.expand = options.expand || config.DefaultExpand as any; + options.top = options.top || config.DefaultTop; + + const segments: { name: string, value: string }[] = []; + // tslint:disable-next-line:forin + for (const key in options) { + const name = ODATA_PARAMS.indexOf(key) > -1 ? `$${key}` : key; + const plainValue = options[key]; + let parsedValue = plainValue; + if (plainValue instanceof Array && plainValue.length && plainValue.length > 0) { + parsedValue = plainValue.map((v) => v.join && v.join(' ') || v).join(','); + } + if (name && parsedValue && parsedValue.length) { + segments.push({ name, value: parsedValue }); + } } - } - return segments.map(s => `${s.name}=${s.value}`).join('&'); -} + return segments.map((s) => `${s.name}=${s.value}`).join('&'); + }; /** * Method that gets the URL that refers to a single item in the Sense/Net Content Repository @@ -73,20 +76,22 @@ export const getContentURLbyPath: (path: string) => string = (path: string): str if (typeof path === 'undefined' || path.indexOf('/') < 0 || path.length <= 1) { throw new Error('This is not a valid path.'); } - if (isItemPath(path)) + if (isItemPath(path)) { return path; + } - let lastSlashPosition = path.lastIndexOf('/'); - let name = path.substring(lastSlashPosition + 1); - let parentPath = path.substring(0, lastSlashPosition); + const lastSlashPosition = path.lastIndexOf('/'); + const name = path.substring(lastSlashPosition + 1); + const parentPath = path.substring(0, lastSlashPosition); let url; - if (name.indexOf('Root') > -1) + if (name.indexOf('Root') > -1) { url = `${parentPath}/('${name}')`; - else - url = `${parentPath}('${name}')` + } else { + url = `${parentPath}('${name}')`; + } return url; -} +}; /** * Method that gets the URL that refers to a single item in the Sense/Net Content Repository by its Id * @param id {number} Id of the Content. @@ -94,7 +99,7 @@ export const getContentURLbyPath: (path: string) => string = (path: string): str */ export const getContentUrlbyId: (id: number) => string = (id: number) => { return `/content(${id})`; -} +}; /** * Method that tells if a path is an item path. * @param path {string} Path that you want to test. @@ -102,7 +107,7 @@ export const getContentUrlbyId: (id: number) => string = (id: number) => { */ export const isItemPath: (path: string) => boolean = (path) => { return path.indexOf("('") >= 0 && path.indexOf("')") === path.length - 2; -} +}; /** * Method that allows to join paths without multiple or missing slashes @@ -111,13 +116,13 @@ export const isItemPath: (path: string) => boolean = (path) => { export const joinPaths = (...args: string[]) => { const trimSlashes = (path: string) => { if (path.endsWith('/')) { - path = path.substring(0, path.length - 1) + path = path.substring(0, path.length - 1); } if (path.startsWith('/')) { path = path.substring(1, path.length); } return path; - } + }; return args.map(trimSlashes).join('/'); -} \ No newline at end of file +}; diff --git a/src/Query/Query.ts b/src/Query/Query.ts index 7031d95..968d672 100644 --- a/src/Query/Query.ts +++ b/src/Query/Query.ts @@ -1,12 +1,12 @@ /** * @module Query - * */ /** */ + */ /** */ -import { Content } from '../Content'; -import { QuerySegment, QueryExpression, QueryResult } from '.'; -import { BaseRepository } from '../Repository/BaseRepository'; +import { Observable } from 'rxjs/Observable'; +import { QueryExpression, QueryResult, QuerySegment } from '.'; +import { IContent } from '../Content'; import { IODataParams } from '../ODataApi'; -import { Observable } from '@reactivex/rxjs'; +import { BaseRepository } from '../Repository/BaseRepository'; /** * Represents an instance of a Query expression. @@ -16,7 +16,7 @@ import { Observable } from '@reactivex/rxjs'; * console.log(query.toString()); // the content query expression * ``` */ -export class Query{ +export class Query { private readonly _segments: QuerySegment[] = []; /** @@ -30,8 +30,9 @@ export class Query{ /** * @returns {String} The Query expression as a sensenet Content Query */ - public toString(): string{ - return this._segments.map(s => s.toString()).join(''); + // tslint:disable-next-line:naming-convention + public toString(): string { + return this._segments.map((s) => s.toString()).join(''); } constructor(build: (first: QueryExpression) => void) { @@ -46,17 +47,17 @@ export class Query{ * @param {ODataParams} odataParams Additional OData parameters (like $select, $expand, etc...) * @returns {Observable>} An Observable that will publish the Query result */ - public Exec(repository: BaseRepository, path: string, odataParams: IODataParams = {}): Observable>{ + public Exec(repository: BaseRepository, path: string, odataParams: IODataParams = {}): Observable> { odataParams.query = this.toString(); - return repository.GetODataApi().Fetch({ + return repository.GetODataApi().Fetch({ path, params: odataParams - }, Content) - .map(q => { + }) + .map((q) => { return { - Result: q.d.results.map(c => repository.HandleLoadedContent(c as any)), + Result: q.d.results.map((c) => repository.HandleLoadedContent(c)), Count: q.d.__count - } + }; }); } } @@ -64,11 +65,11 @@ export class Query{ /** * Represents a finialized Query instance that has a Repository, path and OData Parameters set up */ -export class FinializedQuery extends Query{ - constructor(build: (first: QueryExpression) => void, - private readonly _repository: BaseRepository, - private readonly _path: string, - private readonly _odataParams: IODataParams = {}) { +export class FinializedQuery extends Query { + constructor(build: (first: QueryExpression) => void, + private readonly _repository: BaseRepository, + private readonly _path: string, + private readonly _odataParams: IODataParams = {}) { super(build); } @@ -85,4 +86,4 @@ export class FinializedQuery extends Query{ public Exec(): Observable> { return super.Exec(this._repository, this._path, this._odataParams); } -} \ No newline at end of file +} diff --git a/src/Query/QueryResult.ts b/src/Query/QueryResult.ts index 0ccf805..77df0bd 100644 --- a/src/Query/QueryResult.ts +++ b/src/Query/QueryResult.ts @@ -1,18 +1,18 @@ /** * @module Query - * */ /** */ -import { Content, SavedContent } from '../Content'; + */ /** */ +import { IContent, SavedContent } from '../Content'; /** * Represents a Content Query result */ -export class QueryResult{ +export class QueryResult { /** * The result yielded by the Query */ - Result: SavedContent[]; + public Result: SavedContent[]; /** * The item count */ - Count: number; -} \ No newline at end of file + public Count: number; +} diff --git a/src/Query/QuerySegment.ts b/src/Query/QuerySegment.ts index a652299..dee4288 100644 --- a/src/Query/QuerySegment.ts +++ b/src/Query/QuerySegment.ts @@ -1,21 +1,21 @@ /** * @module Query - * */ /** */ + */ /** */ import { Query } from '.'; -import { Content, isContent } from '../Content'; +import { IContent, SavedContent } from '../Content'; /** * Represents a query expression segment */ -export class QuerySegment{ +export class QuerySegment { /** * Escapes a String value (except '?' and '*' characters for wildcards) * @param {string} value The String value to be escaped * @returns {string} The escaped value */ - protected escapeValue(value: string): string{ + protected escapeValue(value: string): string { return typeof value === 'string' ? value.replace(/([\!\+\&\|\(\)\[\]\{\}\^\~\:\"])/g, '\\$1') : value; } @@ -26,7 +26,7 @@ export class QuerySegment{ * @param {K} field The name of the field * @param {boolean} reverse Sort in reverse order, false by default */ - public Sort(field: K, reverse: boolean = false){ + public Sort(field: K, reverse: boolean = false) { this._stringValue = ` .${reverse ? 'REVERSESORT' : 'SORT'}:'${field}'`; return this.finializeSegment(); } @@ -35,7 +35,7 @@ export class QuerySegment{ * A '.TOP' Content Query segment * @param {number} topCount The TOP item count */ - public Top(topCount: number){ + public Top(topCount: number) { this._stringValue = ` .TOP:${topCount}`; return this.finializeSegment(); } @@ -45,7 +45,7 @@ export class QuerySegment{ * @param {number} skipCount Items to skip */ - public Skip(skipCount: number){ + public Skip(skipCount: number) { this._stringValue = ` .SKIP:${skipCount}`; return this.finializeSegment(); } @@ -53,7 +53,8 @@ export class QuerySegment{ /** * @returns {string} a segment string value */ - public toString(){ + // tslint:disable-next-line:naming-convention + public toString() { return this._stringValue; } @@ -71,14 +72,14 @@ export class QuerySegment{ /** * Represents a sensenet Content Query expression */ -export class QueryExpression extends QuerySegment { +export class QueryExpression extends QuerySegment { /** * A plain string as Query term * @param {string} term The Query term * @returns { QueryOperator } The Next query operator (fluent) */ - Term(term: string){ + public Term(term: string) { this._stringValue = term; return this.finialize(); } @@ -88,8 +89,8 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - InTree(path: string | Content){ - const pathValue = this.escapeValue(isContent(path) && path.Path ? path.Path : path.toString()) + public InTree(path: string | SavedContent) { + const pathValue = this.escapeValue((path as SavedContent).Path ? (path as SavedContent).Path : path.toString()); this._stringValue = `InTree:"${pathValue}"`; return this.finialize(); } @@ -99,8 +100,8 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - InFolder(path: string | Content){ - const pathValue = this.escapeValue(isContent(path) && path.Path ? path.Path : path.toString()) + public InFolder(path: string | SavedContent) { + const pathValue = this.escapeValue((path as SavedContent).Path ? (path as SavedContent).Path : path.toString()); this._stringValue = `InFolder:"${pathValue}"`; return this.finialize(); } @@ -111,9 +112,9 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - Type(newTypeAssertion: { new(...args: any[]): TNewType }) { + public Type(newTypeAssertion: { new(...args: any[]): TNewType }) { this._stringValue = `Type:${newTypeAssertion.name}`; - return this.finialize() + return this.finialize(); } /** @@ -121,9 +122,9 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - TypeIs(newTypeAssertion: { new(...args: any[]): TNewType }) { + public TypeIs(newTypeAssertion: { new(...args: any[]): TNewType }) { this._stringValue = `TypeIs:${newTypeAssertion.name}`; - return this.finialize() + return this.finialize(); } /** @@ -132,7 +133,7 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - Equals(fieldName: K | '_Text', value: TReturns[K]){ + public Equals(fieldName: K | '_Text', value: TReturns[K]) { this._stringValue = `${fieldName}:'${this.escapeValue(value)}'`; return this.finialize(); } @@ -144,7 +145,7 @@ export class QueryExpression extends QuerySegment } The Next query operator (fluent) */ - NotEquals(fieldName: K, value: TReturns[K]){ + public NotEquals(fieldName: K, value: TReturns[K]) { this._stringValue = `NOT(${fieldName}:'${this.escapeValue(value)}')`; return this.finialize(); } @@ -157,31 +158,29 @@ export class QueryExpression extends QuerySegment(fieldName: K, minValue: TReturns[K], maxValue: TReturns[K], minimumInclusive: boolean = false, maximumInclusive: boolean = false){ + public Between(fieldName: K, minValue: TReturns[K], maxValue: TReturns[K], minimumInclusive: boolean = false, maximumInclusive: boolean = false) { this._stringValue = `${fieldName}:${minimumInclusive ? '[' : '{'}'${this.escapeValue(minValue)}' TO '${this.escapeValue(maxValue)}'${maximumInclusive ? ']' : '}'}`; return this.finialize(); } - /** * Greather than query expression (+FieldName:>'value') * @param { K } fieldName he name of the Field to be checked * @param { TReturns[K] } minValue The minimum allowed value * @param { boolean } minimumInclusive Lower limit will be inclusive / exclusive */ - GreatherThan(fieldName: K, minValue: TReturns[K], minimumInclusive: boolean = false){ + public GreatherThan(fieldName: K, minValue: TReturns[K], minimumInclusive: boolean = false) { this._stringValue = `${fieldName}:>${minimumInclusive ? '=' : ''}'${this.escapeValue(minValue)}'`; return this.finialize(); } - /** * Less than query expression (+FieldName:<'value') * @param { K } fieldName he name of the Field to be checked * @param { TReturns[K] } maxValue The maximum allowed value * @param { boolean } maximumInclusive Upper limit will be inclusive / exclusive */ - LessThan(fieldName: K, maxValue: TReturns[K], maximumInclusive: boolean = false){ + public LessThan(fieldName: K, maxValue: TReturns[K], maximumInclusive: boolean = false) { this._stringValue = `${fieldName}:<${maximumInclusive ? '=' : ''}'${this.escapeValue(maxValue)}'`; return this.finialize(); } @@ -190,7 +189,7 @@ export class QueryExpression extends QuerySegment) => QuerySegment)} build The Expression builder method */ - Query(build: (first: QueryExpression) => QuerySegment){ + public Query(build: (first: QueryExpression) => QuerySegment) { const innerQuery = new Query(build); this._stringValue = `(${innerQuery.toString()})`; return this.finialize(); @@ -200,20 +199,20 @@ export class QueryExpression extends QuerySegment) => QuerySegment)} build The Expression builder method */ - Not(build: (first: QueryExpression) => QuerySegment){ + public Not(build: (first: QueryExpression) => QuerySegment) { const innerQuery = new Query(build); this._stringValue = `NOT(${innerQuery.toString()})`; return this.finialize(); } - protected finialize(): QueryOperators { + protected finialize(): QueryOperators { this._queryRef.AddSegment(this); return new QueryOperators(this._queryRef as any as Query); } } // And, Or, Etc... -export class QueryOperators extends QuerySegment{ +export class QueryOperators extends QuerySegment { /** * AND Content Query operator @@ -223,7 +222,6 @@ export class QueryOperators extends QuerySegment extends QuerySegment; public readonly Events: RepositoryEventHub = new RepositoryEventHub(); - /** * Returns the Repository's base OData Url (e.g.: https://demo.sensenet.com/odata.svc) */ + // tslint:disable-next-line:naming-convention public get ODataBaseUrl() { return ODataHelper.joinPaths(this.Config.RepositoryUrl, this.Config.ODataToken); } public WaitForAuthStateReady() { - return this.Authentication.State.skipWhile(state => state === Authentication.LoginState.Pending) + return this.Authentication.State.skipWhile((state) => state === Authentication.LoginState.Pending) .first(); } @@ -45,28 +48,28 @@ export class BaseRepository} An observable, which will be updated with the response. */ public Ajax(path: string, - method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', - returnsType?: { new(...args: any[]): T }, - body?: any, - additionalHeaders?: { name: string, value: string }[] + method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', + returnsType?: { new(...args: any[]): T }, + body?: any, + additionalHeaders?: { name: string, value: string }[] ): Observable { this.Authentication.CheckForUpdate(); return this.WaitForAuthStateReady() - .flatMap(state => { + .flatMap((state) => { if (!returnsType) { returnsType = Object as { new(...args: any[]): any }; } return this.HttpProviderRef.Ajax(returnsType, { url: ODataHelper.joinPaths(this.ODataBaseUrl, path), - method: method, - body: body, + method, + body, responseType: 'json', }, additionalHeaders); }); } - public UploadFile(uploadOptions: WithParentContent>): Observable> { + public UploadFile(uploadOptions: WithParentContent>): Observable> { this.Authentication.CheckForUpdate(); uploadOptions.Body = { ...uploadOptions.Body, @@ -74,11 +77,11 @@ export class BaseRepository { - const uploadSubject = new Subject>(); + .flatMap((state) => { + const uploadSubject = new Subject>(); const fileName = uploadOptions.File.name; const uploadPath = ODataHelper.joinPaths(this.ODataBaseUrl, uploadOptions.Parent.GetFullPath(), 'upload'); @@ -86,12 +89,12 @@ export class BaseRepository((uploadOptions.ContentType || ContentInternal) as { new(...args: any[]): Content }, uploadOptions.File, { url: uploadPath, body: uploadOptions.Body, }) - .subscribe(created => { - this.HandleLoadedContent(created as T & { Id: number, Path: string }, uploadOptions.ContentType).Reload().subscribe(c => { + .subscribe((created) => { + this.HandleLoadedContent(created as SavedContent).Reload().subscribe((c) => { this.Events.Trigger.ContentCreated({ Content: c }); @@ -101,13 +104,13 @@ export class BaseRepository { + }, (error) => { this.Events.Trigger.ContentCreateFailed({ Content: { Id: null, @@ -115,9 +118,9 @@ export class BaseRepository { + flatMap((chunkToken) => { const resp = new UploadResponse(...chunkToken.split('*')); + const createdContent: SavedContent = this.HandleLoadedContent({ + Id: resp.ContentId, + Path: uploadOptions.Parent.Path, + Name: uploadOptions.File.name, + Type: uploadOptions.ContentType.name + } as TFile & ISavedContent); this.Events.Trigger.ContentCreated({ - Content: this.HandleLoadedContent({ - Id: resp.ContentId, - Path: uploadOptions.Parent.Path, - Name: uploadOptions.File.name, - }, uploadOptions.ContentType) - }) + Content: createdContent + }); return this.sendChunk(uploadOptions, uploadPath, chunkToken.toString(), resp.ContentId) - .flatMap(c => { - return this.Load(resp.ContentId, undefined, uploadOptions.ContentType) - .map(content => { + .flatMap((c) => { + return this.Load(resp.ContentId) + .map((content) => { const chunkCount = Math.ceil(uploadOptions.File.size / this.Config.ChunkSize); + // tslint:disable-next-line:no-string-literal content['_isOperationInProgress'] = false; const progressInfo = { Completed: true, ChunkCount: chunkCount, UploadedChunks: chunkCount, - CreatedContent: content as T - } as UploadProgressInfo + CreatedContent: content + } as UploadProgressInfo; this.Events.Trigger.UploadProgress(progressInfo); return progressInfo; }); - }) - }) + }); + }); } return uploadSubject.asObservable(); - }) + }); } - private sendChunk - (options: WithParentContent>, uploadPath: string, chunkToken: string, contentId: number, offset: number = 0) + private sendChunk(options: WithParentContent>, uploadPath: string, chunkToken: string, contentId: number, offset: number = 0) : Observable> { this.Authentication.CheckForUpdate(); return this.WaitForAuthStateReady() - .flatMap(state => { + .flatMap((state) => { let chunkEnd = offset + this.Config.ChunkSize; chunkEnd = chunkEnd > options.File.size ? options.File.size : chunkEnd; @@ -198,12 +203,14 @@ export class BaseRepository { - const content = this.HandleLoadedContent({ + }).map((newResp) => { + const content = this.HandleLoadedContent({ Id: contentId, - Path: '', - Name: options.File.name, - }, options.ContentType); + Path: 'asd', + Name: options.File.name || 'File', + Type: options.ContentType.name || 'File' + } as T & ISavedContent); + // tslint:disable-next-line:no-string-literal content['_isOperationInProgress'] = true; const progress = { Completed: false, @@ -218,11 +225,11 @@ export class BaseRepository this.sendChunk(options, uploadPath, chunkToken, contentId, offset + this.Config.ChunkSize)); - }) + return request.flatMap((r) => this.sendChunk(options, uploadPath, chunkToken, contentId, offset + this.Config.ChunkSize)); + }); } - public UploadTextAsFile(options: UploadTextOptions & { Parent: Content }) { + public UploadTextAsFile(options: WithParentContent>) { const file = new File([options.Text], options.FileName); return this.UploadFile({ File: file, @@ -230,43 +237,42 @@ export class BaseRepository(FileEntry: WebKitFileEntry, Scope: Content, options: UploadOptions) { + private async webkitFileHandler(FileEntry: WebKitFileEntry, Scope: Content, options: UploadOptions) { await new Promise((resolve, reject) => { - FileEntry.file(f => { + FileEntry.file((f) => { Scope.UploadFile({ File: f as any, ...options }) - .skipWhile(progress => !progress.Completed) - .subscribe( - progress => resolve(progress), - err => reject(err)); - }, err => reject(err)); - }) + .skipWhile((progress) => !progress.Completed) + .subscribe( + (progress) => resolve(progress), + (err) => reject(err)); + }, (err) => reject(err)); + }); } - private async webkitDirectoryHandler(Directory: WebKitDirectoryEntry, Scope: Content, options: UploadOptions) { + private async webkitDirectoryHandler(Directory: WebKitDirectoryEntry, Scope: Content, options: UploadOptions) { await new Promise((resolve, reject) => { - this.CreateContent({ + this.CreateContent({ Name: Directory.name, Path: Scope.Path, DisplayName: Directory.name - }, ContentTypes.Folder).Save().subscribe(async c => { + }, Folder).Save().subscribe(async (c) => { const dirReader = Directory.createReader(); await new Promise((res) => { - dirReader.readEntries(async items => { - await this.webkitItemListHandler(items as any, c, true, options); + dirReader.readEntries(async (items) => { + await this.webkitItemListHandler(items as any, c, true, options); res(); - }) + }); }); resolve(c); - }, err => reject(err)); - }) + }, (err) => reject(err)); + }); } - private async webkitItemListHandler(items: (WebKitFileEntry | WebKitDirectoryEntry)[], Scope: Content, CreateFolders: boolean, options: UploadOptions) { + private async webkitItemListHandler(items: (WebKitFileEntry | WebKitDirectoryEntry)[], Scope: Content, CreateFolders: boolean, options: UploadOptions) { + // tslint:disable-next-line:forin for (const index in items) { if (CreateFolders && items[index].isDirectory) { await this.webkitDirectoryHandler(items[index] as WebKitDirectoryEntry, Scope, options); @@ -277,10 +283,10 @@ export class BaseRepository(options: UploadFromEventOptions & { Parent: Content }) { + public async UploadFromDropEvent(options: UploadFromEventOptions & { Parent: Content }) { if ((window as any).webkitRequestFileSystem) { const entries: (WebKitFileEntry | WebKitDirectoryEntry)[] = - [].map.call(options.Event.dataTransfer.items, i => i.webkitGetAsEntry()); + [].map.call(options.Event.dataTransfer.items, (i) => i.webkitGetAsEntry()); await this.webkitItemListHandler(entries, options.Parent, options.CreateFolders, options); } else { @@ -290,12 +296,10 @@ export class BaseRepository - }).subscribe(c => { - - }); + }).subscribe((c) => { /**/ }); } - }) + }); } } @@ -309,10 +313,12 @@ export class BaseRepository { - console.warn('The property repository.Content is deprecated and will be removed in the near future. Use repositoy.GetODataApi() instead.') + // tslint:disable-next-line:no-console + console.warn('The property repository.Content is deprecated and will be removed in the near future. Use repositoy.GetODataApi() instead.'); return this._odataApi; - }; + } + // tslint:disable-next-line:naming-convention public GetODataApi(): ODataApi { return this._odataApi; } @@ -333,15 +339,15 @@ export class BaseRepository, - _httpProviderType: { new(): TProviderType }, - authentication: { new(...args: any[]): TAuthenticationServiceType }) { + _httpProviderType: { new(): TProviderType }, + authentication: { new(...args: any[]): TAuthenticationServiceType }) { this.HttpProviderRef = new _httpProviderType(); this.Config = new SnConfigModel(config); - //warning: Authentication constructor parameterization is not type-safe + // warning: Authentication constructor parameterization is not type-safe this.Authentication = new authentication(this.HttpProviderRef, this.Config.RepositoryUrl, this.Config.JwtTokenKeyTemplate, this.Config.JwtTokenPersist); - this._odataApi = new ODataApi(_httpProviderType, this); + this._odataApi = new ODataApi(this); this.initUserUpdate(); } @@ -386,13 +392,12 @@ export class BaseRepository { - return resp.d.results.map(c => this.HandleLoadedContent(c as any, ContentType)); + .map((resp) => { + return resp.d.results.map((c) => this.HandleLoadedContent(c)); }); } - - private _loadedContentReferenceCache: SavedContent[] = []; + private _loadedContentReferenceCache: Map = new Map(); /** * Creates a Content instance that is loaded from the Repository. This method should be used only to instantiate content from payload received from the backend. @@ -403,25 +408,29 @@ export class BaseRepository(opt: O & { Id: number, Path: string }, contentType?: { new(...args: any[]): T }): SavedContent { - let instance: T; - const realContentType = (contentType || (opt.Type && (ContentTypes as any)[opt.Type]) || Content) as { new(...args: any[]): T }; + public HandleLoadedContent(opt: T & ISavedContent, contentType?: { new(...args: any[]): T }): SavedContent { + let instance: Content; + + const realContentType = (contentType || (opt.Type && (ContentTypes as any)[opt.Type]) || Folder) as { new(...args: any[]): T }; if (opt.Id) { - if (this._loadedContentReferenceCache[opt.Id]) { - instance = this._loadedContentReferenceCache[opt.Id] as T; + const cached = this._loadedContentReferenceCache.get(opt.Id); + if (cached) { + instance = cached as Content; + // tslint:disable-next-line:no-string-literal instance['updateLastSavedFields'](opt); } else { - instance = Content.Create(opt, realContentType, this); - this._loadedContentReferenceCache[opt.Id] = instance as SavedContent; + instance = ContentInternal.Create(opt, realContentType, this); + this._loadedContentReferenceCache.set(opt.Id, instance as SavedContent); } } else { - instance = Content.Create(opt, realContentType, this); + instance = ContentInternal.Create(opt, realContentType, this); } + // tslint:disable-next-line:no-string-literal instance['_isSaved'] = true; - this.Events.Trigger.ContentLoaded({ Content: instance }); - return instance as T & { Id: number, Path: string }; + this.Events.Trigger.ContentLoaded({ Content: instance as SavedContent }); + return instance as SavedContent; } /** @@ -442,35 +451,33 @@ export class BaseRepository console.log('done'), * }) * ``` - */ - public Load( + */ + public Load( idOrPath: string | number, odataOptions?: IODataParams, - returnsType?: { new(...args: any[]): TContentType }, version?: string): Observable> { - let contentURL = typeof idOrPath === 'string' ? + const contentURL = typeof idOrPath === 'string' ? ODataHelper.getContentURLbyPath(idOrPath) : ODataHelper.getContentUrlbyId(idOrPath); - let odataRequestOptions = { + const odataRequestOptions = { path: contentURL, params: odataOptions }; - const returnType = returnsType || Content as { new(...args: any[]): any }; - - return this._odataApi.Get(odataRequestOptions, returnType) + return this._odataApi.Get(odataRequestOptions) .share() - .map(r => { - return this.HandleLoadedContent(r.d, returnType); + .map((r) => { + return this.HandleLoadedContent(r.d); }); } /** * Shortcut to Content.Create */ - CreateContent: (options: K, contentType: { new(...args: any[]): T }) => T = - (options, contentType) => Content.Create(options, contentType, this); + public CreateContent: (options: TContentType, contentType: { new(...args: any[]): TContentType }) => Content = + (options: TContentType, contentType: { new(...args: any[]): TContentType }) => + ContentInternal.Create(options, contentType, this) /** * Parses a Content instance from a stringified SerializedContent instance @@ -483,25 +490,27 @@ export class BaseRepository({ Id: 6, DisplayName: 'Visitor', Domain: 'BuiltIn', Name: 'Visitor', Path: '/Root/IMS/BuiltIn/Portal/Visitor', - LoginName: 'Visitor' - }, ContentTypes.User), - PortalRoot: this.HandleLoadedContent({ + LoginName: 'Visitor', + Type: 'User' + }), + PortalRoot: this.HandleLoadedContent({ Id: 2, Path: '/Root', Name: 'Root', - DisplayName: 'Root' - }, ContentTypes.PortalRoot) - } + DisplayName: 'Root', + Type: 'PortalRoot' + }) + }; /** * Creates a Content Query on a Repositoy instance, at Root level (path e.g.: '/OData.svc/Root' ) @@ -517,8 +526,7 @@ export class BaseRepository>} An observable with the Query result. */ - CreateQuery: (build: (first: QueryExpression) => QuerySegment, params?: IODataParams) => FinializedQuery - = (build, params) => new FinializedQuery(build, this, 'Root', params); + public CreateQuery = (build: (first: QueryExpression) => QuerySegment, params?: IODataParams) => new FinializedQuery(build, this, 'Root', params); /** * Executes a DeleteBatch request to delete multiple content by a single request. @@ -534,34 +542,38 @@ export class BaseRepository>)[], permanently: boolean = false, rootContent = this._staticContent.PortalRoot) { - const contentFields = contentList.map(c => c.GetFields()); - const action = this._odataApi.CreateCustomAction({ + public DeleteBatch(contentList: (Content & ISavedContent)[], permanent: boolean = false, rootContent = this._staticContent.PortalRoot) { + const action = this._odataApi.CreateCustomAction({ name: 'DeleteBatch', path: rootContent.Path, isAction: true, requiredParams: ['paths'] }, { - data: [ - { 'paths': contentList.map(c => c.Id || c.Path).filter(c => c !== undefined) }, - { 'permanently': permanently } - ] - }); + data: { + paths: contentList.map((c) => c.Id || c.Path).filter((c) => c !== undefined), + permanent + } - action.subscribe(result => { - contentFields.forEach(contentData => { - this.Events.Trigger.ContentDeleted({ ContentData: contentData, Permanently: permanently }) }); - }, error => { - contentList.forEach(content => { - this.Events.Trigger.ContentDeleteFailed({ Content: content, Error: error, Permanently: permanently }) - }) + + action.subscribe((result) => { + if (result.d.__count) { + result.d.results.forEach((deleted) => { + this.Events.Trigger.ContentDeleted({ ContentData: deleted, Permanently: permanent }); + }); + + result.d.errors.forEach((error) => { + this.Events.Trigger.ContentDeleteFailed({ Content: this.HandleLoadedContent(error.content), Error: error.error, Permanently: permanent }); + }); + } + }, (error) => { + // Whole batch operation failed }); return action; - }; + } /** - * Executes a MoveBatch request to move multiple content by a single request. + * Executes a MoveBatch request to move multiple content by a single request. * * Usage: * ```ts @@ -572,9 +584,8 @@ export class BaseRepository[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { - const contentPathList: string[] = contentList.map(c => c.Path); - const action = this._odataApi.CreateCustomAction({ + public MoveBatch(contentList: (SavedContent<{}>)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { + const action = this._odataApi.CreateCustomAction>({ name: 'MoveBatch', path: rootContent.Path, isAction: true, @@ -582,27 +593,39 @@ export class BaseRepository c.Path).filter(c => c !== undefined), + paths: contentList.map((c) => c.Path).filter((c) => c !== undefined), targetPath }, ] }); + action.subscribe((result) => { + if (result.d.__count) { + result.d.results.forEach((moved) => { + this.Events.Trigger.ContentMoved({ + From: (contentList.find((a) => a.Id === moved.Id) as SavedContent).Path, + Content: this.HandleLoadedContent(moved), + To: targetPath + }); + }); - action.subscribe(result => { - contentPathList.forEach((path, index) => { - this.Events.Trigger.ContentMoved({ From: path, Content: contentList[index], To: targetPath }) - }); - }, error => { - contentList.forEach((contentData, index) => { - this.Events.Trigger.ContentMoveFailed({ From: contentData.Path, Content: contentList[index], To: targetPath, Error: error }) - }) + result.d.errors.forEach((error) => { + this.Events.Trigger.ContentMoveFailed({ + From: (contentList.find((a) => a.Id === error.content.Id) as SavedContent).Path, + Content: this.HandleLoadedContent(error.content), + To: targetPath, + Error: error.error + }); + }); + } + }, (error) => { + // Whole batch operation failed }); return action; } /** - * Executes a CopyBatch request to copy multiple content by a single request. + * Executes a CopyBatch request to copy multiple content by a single request. * * Usage: * ```ts @@ -613,9 +636,8 @@ export class BaseRepository[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { - const contentFields = contentList.map(c => c.GetFields()); - const action = this._odataApi.CreateCustomAction({ + public CopyBatch(contentList: (SavedContent)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { + const action = this._odataApi.CreateCustomAction({ name: 'CopyBatch', path: rootContent.Path, isAction: true, @@ -623,51 +645,50 @@ export class BaseRepository c.Path).filter(c => c !== undefined), + paths: contentList.map((c) => c.Path).filter((c) => c !== undefined), targetPath }, ] }); - action.subscribe(result => { - contentFields.forEach((contentData, index) => { - // ToDo: Update from CopyBatch response - const created = this.HandleLoadedContent(contentData as any); - created.Path = ODataHelper.joinPaths(targetPath, created.Name || ''); - (created as any).Id = undefined; - this.Events.Trigger.ContentCreated({ Content: created }); - }); - }, error => { - contentList.forEach((contentData, index) => { - this.Events.Trigger.ContentCreateFailed({ Error: error, Content: contentData }) - }) + action.subscribe((result) => { + if (result.d.__count) { + result.d.results.forEach((created) => { + this.Events.Trigger.ContentCreated({ Content: this.HandleLoadedContent(created) }); + }); + + result.d.errors.forEach((error) => { + this.Events.Trigger.ContentCreateFailed({ Content: error.content, Error: error.error }); + }); + } + }, (error) => { + // Whole batch operation failed }); return action; } - - private readonly currentUserSubject = new BehaviorSubject(this._staticContent.VisitorUser); - public GetCurrentUser: () => Observable = () => { - return this.currentUserSubject + private readonly _currentUserSubject = new BehaviorSubject>(this._staticContent.VisitorUser); + public GetCurrentUser: () => Observable> = () => { + return this._currentUserSubject .distinctUntilChanged() - .filter(u => { + .filter((u) => { const [userDomain, userName] = this.Authentication.CurrentUser.split('\\'); - return u.LoginName === userName && u.Domain === userDomain + return u.LoginName === userName && u.Domain === userDomain; }); } private _lastKnownUserName = 'BuiltIn\\Visitor'; private initUserUpdate() { - this.Authentication.State.skipWhile(state => state === Authentication.LoginState.Pending) - .subscribe(state => { + this.Authentication.State.skipWhile((state) => state === Authentication.LoginState.Pending) + .subscribe((state) => { if (state === LoginState.Unauthenticated) { - this.currentUserSubject.next(this._staticContent.VisitorUser); + this._currentUserSubject.next(this._staticContent.VisitorUser); this._lastKnownUserName = 'BuiltIn\\Visitor'; } else { if (this._lastKnownUserName !== this.Authentication.CurrentUser) { const [userDomain, userName] = this.Authentication.CurrentUser.split('\\'); - this.CreateQuery(q => q.TypeIs(ContentTypes.User) + this.CreateQuery((q) => q.TypeIs(User) .And .Equals('Domain', userDomain) .And @@ -677,18 +698,64 @@ export class BaseRepository { + .subscribe((usr) => { if (usr.Count === 1) { - this.currentUserSubject.next(usr.Result[0]); + this._currentUserSubject.next(usr.Result[0]); this._lastKnownUserName = this.Authentication.CurrentUser; } else { - this.currentUserSubject.error(`Error getting current user: found multiple users with login name '${userName}' in domain '${userDomain}'`) + this._currentUserSubject.error(`Error getting current user: found multiple users with login name '${userName}' in domain '${userDomain}'`); } - }) + }); } } - }) + }); + } + + private _schemaCache: Map; + private _schemaStore: Schema[]; + + public SetSchemas(newSchemas: Schema[]) { + this._schemaStore = newSchemas; + this._schemaCache = new Map(); + } + + /** + * Returns the Content Type Schema of the given Content Type; + * @param type {string} The name of the Content Type; + * @returns {Schemas.Schema} + * ```ts + * var genericContentSchema = SenseNet.Content.getSchema(Content); + * ``` + */ + public GetSchema(currentType: { new(...args: any[]): TType }): Schema { + return this.GetSchemaByName(currentType.name); + } + + public GetSchemaByName(schemaName: string) { + if (!this._schemaCache) { + this._schemaCache = new Map(); + } + + if (!this._schemaStore) { + this._schemaStore = SchemaStore.map((s) => s); + } + + if (this._schemaCache.has(schemaName)) { + return Object.assign({}, this._schemaCache.get(schemaName)) as Schema; + } + let schema = this._schemaStore.find((s) => s.ContentTypeName === schemaName) as Schema; + if (!schema) { + return this.GetSchema(GenericContent); + } + schema = Object.assign({}, schema); + const parentSchema = schema.ParentTypeName && this.GetSchemaByName(schema.ParentTypeName); + + if (parentSchema) { + schema.FieldSettings = [...schema.FieldSettings, ...parentSchema.FieldSettings]; + } + this._schemaCache.set(schemaName, schema); + return schema; } } diff --git a/src/Repository/RepositoryEventHub.ts b/src/Repository/RepositoryEventHub.ts index 6daf9f2..ee578c9 100644 --- a/src/Repository/RepositoryEventHub.ts +++ b/src/Repository/RepositoryEventHub.ts @@ -3,164 +3,167 @@ */ /** */ +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; -import { Content, IContentOptions, SavedContent } from '../Content'; -import { Subject, Observable } from '@reactivex/rxjs'; -import { IODataParams, ICustomActionOptions } from '../ODataApi'; +import { IContent, SavedContent } from '../Content'; +import { ICustomActionOptions, IODataParams } from '../ODataApi'; import { UploadProgressInfo } from './UploadModels'; - -export module EventModels { +// tslint:disable-next-line:no-namespace +export namespace EventModels { export class Created { /** * The created Content instance */ - Content: SavedContent; + public Content: SavedContent; } export class CreateFailed { /** * The unsaved Content instance */ - Content: Content; + public Content: IContent; /** * The Error that caused the failure */ - Error: any + public Error: any; } export class Modified { /** * The Content instance that has been modified. */ - Content: SavedContent; + public Content: SavedContent; /** * The original fields */ - OriginalFields: IContentOptions; + public OriginalFields: IContent; /** * The Change data */ - Changes: IContentOptions; + public Changes: IContent; } export class ModificationFailed { /** * The Content instance that has been failed to modify */ - Content: SavedContent; + public Content: SavedContent; /** * The Fields that you've been tried to modify */ - Fields?: IContentOptions; + public Fields?: IContent; /** * The Error that caused the failure */ - Error: any + public Error: any; } export class Loaded { /** * The Loaded content instance */ - Content: SavedContent + public Content: SavedContent; } export class Deleted { /** * The Content data that has been deleted */ - ContentData: IContentOptions; + public ContentData: IContent; /** * Indicates if the Content was deleted permanently or just moved to Trash */ - Permanently: boolean; + public Permanently: boolean; } export class DeleteFailed { /** * The Content that you've tried to delete */ - Content: SavedContent; + public Content: SavedContent; /** * Indicates if you've tried to delete the Content permanently or just tried to move it to the Trash */ - Permanently: boolean; + public Permanently: boolean; /** * The Error that caused the failure */ - Error: any; + public Error: any; } - export class CustomActionExecuted { + export class CustomActionExecuted { /** * The Action options */ - ActionOptions: ICustomActionOptions; + public ActionOptions: ICustomActionOptions; /** * The additional OData parameters (optional) */ - ODataParams?: IODataParams; + // tslint:disable-next-line:naming-convention + public ODataParams?: IODataParams; /** * The Action result */ - Result: any + public Result: any; } - export class CustomActionFailed { + export class CustomActionFailed { /** * The Action options */ - ActionOptions: ICustomActionOptions; + public ActionOptions: ICustomActionOptions; /** * The additional OData parameters (optional) */ - ODataParams?: IODataParams; + // tslint:disable-next-line:naming-convention + public ODataParams?: IODataParams; /** * The Type of the Result object */ - ResultType: { new(...args: any[]): Object }; + public ResultType: { new(...args: any[]): any }; /** * The Error that caused the failure */ - Error: any + public Error: any; } export class ContentMoved { /** * The From path (the original Parent's Path) */ - From: string; + public From: string; /** * The destination path (the new Parent's Path) */ - To: string; + public To: string; /** * The moved Content instance */ - Content: SavedContent; + public Content: SavedContent; } export class ContentMoveFailed { /** * The From path (the original Parent's Path) */ - From: string; + public From: string; /** * The destination path (the new Parent's Path) */ - To: string; + public To: string; /** * The Content instance that you've tried to move */ - Content: SavedContent; + public Content: SavedContent; /** * The Error that caused the failure */ - Error: any; + public Error: any; } } @@ -172,11 +175,11 @@ export class RepositoryEventHub { private readonly _onContentLoadedSubject = new Subject(); private readonly _onContentDeletedSubject = new Subject(); private readonly _onContentDeleteFailedSubject = new Subject(); - private readonly _onCustomActionExecutedSubject = new Subject>(); - private readonly _onCustomActionFailedSubject = new Subject>(); - private readonly _onContentMovedSubject = new Subject() - private readonly _onContentMoveFailedSubject = new Subject() - private readonly _onUploadProgressSubject = new Subject>() + private readonly _onCustomActionExecutedSubject = new Subject>(); + private readonly _onCustomActionFailedSubject = new Subject>(); + private readonly _onContentMovedSubject = new Subject(); + private readonly _onContentMoveFailedSubject = new Subject(); + private readonly _onUploadProgressSubject = new Subject>(); /** * Method group for triggering Repository events @@ -188,7 +191,7 @@ export class RepositoryEventHub { ContentModified: (ev: EventModels.Modified) => this._onContentModifiedSubject.next(ev), ContentModificationFailed: (ev: EventModels.ModificationFailed) => this._onContentModificationFailedSubject.next(ev), - ContentLoaded: (ev) => this._onContentLoadedSubject.next(ev), + ContentLoaded: (ev: EventModels.Loaded) => this._onContentLoadedSubject.next(ev), ContentDeleted: (ev: EventModels.Deleted) => this._onContentDeletedSubject.next(ev), ContentDeleteFailed: (ev: EventModels.DeleteFailed) => this._onContentDeleteFailedSubject.next(ev), @@ -199,12 +202,12 @@ export class RepositoryEventHub { ContentMoved: (ev: EventModels.ContentMoved) => this._onContentMovedSubject.next(ev), ContentMoveFailed: (ev: EventModels.ContentMoveFailed) => this._onContentMoveFailedSubject.next(ev), - UploadProgress: (ev: UploadProgressInfo) => this._onUploadProgressSubject.next(ev) - } + UploadProgress: (ev: UploadProgressInfo) => this._onUploadProgressSubject.next(ev) + }; /** - * Triggered after a succesful Content creation - */ + * Triggered after a succesful Content creation + */ public OnContentCreated: Observable = this._onContentCreatedSubject.asObservable(); /** @@ -237,7 +240,6 @@ export class RepositoryEventHub { */ public OnContentDeleteFailed = this._onContentDeleteFailedSubject.asObservable(); - /** * Triggered after moving a content to another location */ @@ -263,5 +265,4 @@ export class RepositoryEventHub { */ public OnUploadProgress = this._onUploadProgressSubject.asObservable(); - -} \ No newline at end of file +} diff --git a/src/Repository/SnRepository.ts b/src/Repository/SnRepository.ts index aca7414..1118c6a 100644 --- a/src/Repository/SnRepository.ts +++ b/src/Repository/SnRepository.ts @@ -2,20 +2,20 @@ * @module Repository */ /** */ -import { BaseRepository } from './'; -import { SnConfigModel } from '../Config/snconfigmodel'; import { JwtService } from '../Authentication/JwtService'; +import { SnConfigModel } from '../Config/snconfigmodel'; import { RxAjaxHttpProvider } from '../HttpProviders/RxAjaxHttpProvider'; +import { BaseRepository } from './'; /** * This class defines a defaul sense NET ECM Repository implementation * that uses an RxJs based Ajax HTTP Provider and a JWT Token Authentication Service */ -export class SnRepository extends BaseRepository{ +export class SnRepository extends BaseRepository { /** * @param {Partial} config The partial config entry used by the repository */ constructor(config?: Partial) { super(new SnConfigModel(config), RxAjaxHttpProvider, JwtService); } -} \ No newline at end of file +} diff --git a/src/Repository/UploadModels.ts b/src/Repository/UploadModels.ts index cd5d2c3..28a4a0f 100644 --- a/src/Repository/UploadModels.ts +++ b/src/Repository/UploadModels.ts @@ -3,107 +3,106 @@ * @module Content */ /** */ -import { Content } from '../Content'; +import { IContent, SavedContent } from '../Content'; -export type WithParentContent = T & { Parent: Content & {Path: string} | {GetFullPath: () => string, Path: string} } +export type WithParentContent = T & { Parent: SavedContent }; -export class UploadOptions{ +export class UploadOptions { /** * The type of the content to upload, usually should be 'File' */ - ContentType: { new(...args: any[]): T }; + public ContentType: { new(...args: any[]): T }; /** * The property to save the Binary data to, usually 'Binary' */ - PropertyName: string; + public PropertyName: string; /** * Sets if the target content should be overwritten */ - Overwrite: boolean; + public Overwrite: boolean; /** * Additional options to post */ - Body?: any; + public Body?: any; } -export class UploadFileOptions extends UploadOptions{ +export class UploadFileOptions extends UploadOptions { /** * The File instance to be posted */ - File: File; + public File: File; } -export class UploadTextOptions extends UploadOptions{ +export class UploadTextOptions extends UploadOptions { /** * The text data that will be saved to the binary field */ - Text: string; + public Text: string; /** * The name of the file */ - FileName: string; + public FileName: string; } -export class UploadFromEventOptions extends UploadOptions{ +export class UploadFromEventOptions extends UploadOptions { /** * The DragEvent to work with. File data will be extracted from it's 'dataTransfer' item. */ - Event: DragEvent; + public Event: DragEvent; /** * Option if folders should be created as well. */ - CreateFolders: boolean; + public CreateFolders: boolean; } -export class UploadProgressInfo { +export class UploadProgressInfo { /** * Basic info about the created Content */ - CreatedContent: T; + public CreatedContent: SavedContent; /** * Total chunks count */ - ChunkCount: number; + public ChunkCount: number; /** * Uploaded chunks */ - UploadedChunks: number; + public UploadedChunks: number; /** * Flag that indicates if the upload has been completed */ - Completed: boolean; + public Completed: boolean; } - export class SaveBinaryFileOptions { /** * The File to be saved */ - File: File; + public File: File; } export class UploadResponse { /** * The ID of the created Content */ - ContentId: number; + public ContentId: number; /** * The Chunk token that can be used during upload */ - ChunkToken: string; + public ChunkToken: string; /** * Flag that indicates if the Content should be finialized after upload */ - MustFinialize: boolean; + public MustFinialize: boolean; /** * Flag that indicates if the Content should be checked in after upload */ - MustCheckin: boolean; + public MustCheckin: boolean; constructor(...args: any[]) { - this.ContentId = parseInt(args[0]); + this.ContentId = parseInt(args[0], 0); this.ChunkToken = args[1]; this.MustFinialize = args[2]; this.MustCheckin = args[3]; } -} \ No newline at end of file +} diff --git a/src/Repository/VersionInfoModels.ts b/src/Repository/VersionInfoModels.ts index a582d02..b345cc3 100644 --- a/src/Repository/VersionInfoModels.ts +++ b/src/Repository/VersionInfoModels.ts @@ -5,11 +5,10 @@ * @description This module contains models for Sense/Net ECM's GetVersionInfo custom action */ /** */ - /** * The type of a specific package */ -export enum PackageType{ +export enum PackageType { /** * Can contain small repeatable activities that do not perform significant changes but can be important because of business or technical reasons. A good example is performing an undo checkout on multiple content. Executing a package of this level does not change the application's or the product's version number but the execution is logged and registered */ @@ -29,7 +28,7 @@ export enum PackageType{ /** * Represents a .NET Assembly in the Version Info */ -export class Assembly{ +export class Assembly { public CodeBase: string; public IsDynamic: boolean; public Name: string; @@ -39,7 +38,7 @@ export class Assembly{ /** * Represents a Sense/NET ECM Component in the Version Info */ -export class Component{ +export class Component { public ComponentId: string; public Version: string; public AcceptableVersion: string; @@ -54,9 +53,9 @@ export class Package { public ComponentVersion: string; public Description: string; public ExecutionDate: Date; - public ExecutionError: Object; + public ExecutionError: any; public Id: number; - public Manifest: Object; + public Manifest: any; public PackageType: PackageType; public ReleaseDate: Date; } @@ -75,4 +74,4 @@ export class VersionInfo { public Components: Component[]; public DatabaseAvailable: boolean; public InstalledPackages: Package[]; -} \ No newline at end of file +} diff --git a/src/Repository/index.ts b/src/Repository/index.ts index 273c89a..bceb072 100644 --- a/src/Repository/index.ts +++ b/src/Repository/index.ts @@ -10,4 +10,4 @@ export * from './UploadModels'; export * from './SnRepository'; export * from './RepositoryEventHub'; export * from './VersionInfoModels'; -export * from './ActionModel' +export * from './ActionModel'; diff --git a/src/Resources.ts b/src/Resources.ts index 88c1351..f05f00c 100644 --- a/src/Resources.ts +++ b/src/Resources.ts @@ -11,6 +11,9 @@ * const editActionName = SenseNet.Resources.Actions["Edit"] * ``` */ /** */ + + // tslint:disable:naming-convention class-name + export class Ctd { } export class CtdApplication { } export class CtdApplicationOverride { } diff --git a/src/Retrier.ts b/src/Retrier.ts index f1307ab..4607a84 100644 --- a/src/Retrier.ts +++ b/src/Retrier.ts @@ -4,8 +4,7 @@ * * @description Module for Retrier. * - * */ /** */ - + */ /** */ /** * Options class for Retrier @@ -36,7 +35,6 @@ export class RetrierOptions { this._retryIntervalMs = v; } - public static readonly TIMEOUT_MS_DEFAULT = 1000; private _timeoutMs: number; /** @@ -63,7 +61,6 @@ export class RetrierOptions { public OnFail?: () => void; } - /** * Utility class for retrying operations. * Usage example: @@ -113,8 +110,8 @@ export class Retrier { * @throws Error if the Retrier is running. * @returns the Retrier instance */ - public Setup(options: Partial){ - if (this._isRunning){ + public Setup(options: Partial) { + if (this._isRunning) { throw Error('Retrier already started!'); } Object.assign(this.Options, options); @@ -128,7 +125,7 @@ export class Retrier { */ public async Run(): Promise { - if (this._isRunning){ + if (this._isRunning) { throw Error('Retrier already started!'); } @@ -146,22 +143,24 @@ export class Retrier { while (!succeeded && !timedOut && (this.Options.Retries > retries)) { retries++; - if (this.Options.OnTry){ + if (this.Options.OnTry) { this.Options.OnTry(); } succeeded = await this._callback(); - !succeeded && await this.wait(this.Options.RetryIntervalMs); + if (!succeeded) { + await this.wait(this.Options.RetryIntervalMs); + } } - if (succeeded){ - if (!timedOut && this.Options.OnSuccess){ + if (succeeded) { + if (!timedOut && this.Options.OnSuccess) { this.Options.OnSuccess(); } } else { - if (this.Options.OnFail){ + if (this.Options.OnFail) { this.Options.OnFail(); } } return succeeded; } -} \ No newline at end of file +} diff --git a/src/SN.ts b/src/SN.ts index d74e142..ca707bd 100644 --- a/src/SN.ts +++ b/src/SN.ts @@ -5,30 +5,43 @@ * @description The main entry module of the package */ /** */ -import * as Authentication from './Authentication'; -import * as ComplexTypes from './ComplexTypes'; -import * as Repository from './Repository'; -import * as ContentTypes from './ContentTypes'; -export * from './Content'; -export * from './ContentSerializer'; -export * from './ContentReferences' -import * as FieldSettings from './FieldSettings'; -export * from './Retrier'; -import * as Schemas from './Schemas'; -import * as Enums from './Enums'; -import * as Collection from './Collection'; -import * as ODataHelper from './ODataHelper'; -import * as ODataApi from './ODataApi'; -import * as Resources from './Resources'; -import * as Security from './Security'; -import * as HttpProviders from './HttpProviders'; -import * as Config from './Config'; -import * as Mocks from '../test/Mocks'; -export * from './Query'; -export * from './ControlMapper'; + import 'rxjs/add/observable/from'; + import 'rxjs/add/observable/of'; + import 'rxjs/add/operator/share'; -export { + import 'rxjs/add/operator/distinctUntilChanged'; + import 'rxjs/add/operator/filter'; + import 'rxjs/add/operator/first'; + import 'rxjs/add/operator/first'; + import 'rxjs/add/operator/map'; + import 'rxjs/add/operator/mergeMap'; + import 'rxjs/add/operator/share'; + import 'rxjs/add/operator/skipWhile'; + + import * as Authentication from './Authentication'; + import * as ComplexTypes from './ComplexTypes'; + import * as ContentTypes from './ContentTypes'; + import * as Repository from './Repository'; + export * from './Content'; + export * from './ContentSerializer'; + export * from './ContentReferences'; + import * as FieldSettings from './FieldSettings'; + export * from './Retrier'; + import * as Mocks from '../test/Mocks'; + import * as Collection from './Collection'; + import * as Config from './Config'; + import * as Enums from './Enums'; + import * as HttpProviders from './HttpProviders'; + import * as ODataApi from './ODataApi'; + import * as ODataHelper from './ODataHelper'; + import * as Resources from './Resources'; + import * as Schemas from './Schemas'; + import * as Security from './Security'; + export * from './Query'; + export * from './ControlMapper'; + + export { Authentication, ComplexTypes, FieldSettings, Schemas, Enums, ContentTypes, Collection, ODataHelper, ODataApi, - Resources, Security, HttpProviders, Repository, Config, Mocks -}; \ No newline at end of file + Resources, Security, HttpProviders, Repository, Config, Mocks, +}; diff --git a/src/Schema/SchemaLoader.ts b/src/Schema/SchemaLoader.ts new file mode 100644 index 0000000..4f52114 --- /dev/null +++ b/src/Schema/SchemaLoader.ts @@ -0,0 +1,49 @@ +import { BinaryField } from '../BinaryField'; +import { ContentType } from '../ContentTypes'; +import { BaseRepository } from '../Repository'; +import { Schema } from '../Schemas'; +import { Content } from '../SN'; + +export class SchemaLoader { + + private static async getCtdXml(contentType: Content): Promise { + return new Promise((resolve, reject) => { + const url = (contentType.Binary as any as BinaryField).GetDownloadUrl(); + contentType.GetRepository().HttpProviderRef.Ajax(String, { + url + }).subscribe((result) => { + try { + const parser = new DOMParser(); + const document = parser.parseFromString(result.toString(), 'text/xml'); + resolve(document); + + } catch (error) { + reject(error); + } + }, (err) => reject(err)); + }); + } + + private static async createSchemaFromCtd(xml: Document): Promise { + /**/ + const schema = new Schema(); + + return schema; + } + + public static async LoadFromRepository(repo: BaseRepository): Promise> { + /** */ + const schemas = new Map(); + + const contentTypes = await repo.CreateQuery((q) => q.TypeIs(ContentType)).Exec().toPromise(); + + for (const contentType in contentTypes.Result) { + if (contentTypes.Result[contentType]) { + const xml = await this.getCtdXml(contentTypes.Result[contentType]); + const schema = await this.createSchemaFromCtd(xml); + schemas.set(schema.ContentTypeName, schema); + } + } + return schemas; + } +} diff --git a/src/Schema/index.ts b/src/Schema/index.ts new file mode 100644 index 0000000..36b089a --- /dev/null +++ b/src/Schema/index.ts @@ -0,0 +1 @@ +export * from './SchemaLoader'; diff --git a/src/Schemas.ts b/src/Schemas.ts index 6d3a13e..70a5b35 100644 --- a/src/Schemas.ts +++ b/src/Schemas.ts @@ -12,3082 +12,3115 @@ * The ```Schema``` class represents an object that holds the basic information about the Content Type (name, icon, ect.) and an array of its ```FieldSettings``` and their full configuration. */ /** */ -import { FieldSettings, Content, ContentTypes } from './SN'; + import * as FieldSettings from './FieldSettings'; - /** - * Class that represents a Schema. - * - * It represents an object that holds the basic information about the Content Type (name, icon, ect.) and an array of its ```FieldSettings``` and their full configuration. - */ - export class Schema { - ContentType: {new(...args: any[]): TContentType} - Icon: string; - DisplayName: string; - Description: string; - AllowIndexing: boolean; - AllowIncrementalNaming: boolean; - AllowedChildTypes: string[]; - FieldSettings: FieldSettings.FieldSetting[]; + export const isSchema: (schema: Schema) => boolean = (schema: Schema): schema is Schema => { + return schema && schema.ContentTypeName && schema.FieldSettings && schema.FieldSettings instanceof Array || false; +}; - constructor(schema: Partial>){ - Object.assign(this, schema); - } - } + /** + * Class that represents a Schema. + * + * It represents an object that holds the basic information about the Content Type (name, icon, ect.) and an array of its ```FieldSettings``` and their full configuration. + */ + export class Schema { + public ContentTypeName: string; + public ParentTypeName?: string; - export const SchemaStore: Schema[] = [ + public Icon: string; + public DisplayName: string; + public Description: string; + public AllowIndexing: boolean; + public AllowIncrementalNaming: boolean; + public AllowedChildTypes: string[]; + public FieldSettings: FieldSettings.FieldSetting[]; + } - /** - * Method that returns the Content Type Definition of the ContentType - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ContentType, - DisplayName: '$Ctd-ContentType,DisplayName', - Description: '$Ctd-ContentType,Description', - Icon: 'ContentType', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.IntegerFieldSetting({ - name: 'Id', - displayName: 'Id', - description: 'A unique ID for the Content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'ParentId', - displayName: 'Id', - description: 'A unique ID for the Content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'VersionId', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Name', - displayName: 'Uri name', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'CreatedById', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'ModifiedById', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Version', - displayName: 'Version', - description: 'Latest version number.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Path', - displayName: 'Path', - description: 'Content type path.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'Depth', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsSystemContent', - displayName: 'System Content', - description: 'This field is true if content is in a system folder/trash or the content is a system folder/file.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'HandlerName', - displayName: 'Handler Name', - description: 'Fully Qualified Name.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'ParentTypeName', - displayName: 'Parent Type Name', - description: 'Name of the parent content type.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'DisplayName', - displayName: 'Name', - description: 'User friendly name of the content type.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'Description', - displayName: 'Description', - description: 'Longer description of the content type.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Icon', - displayName: 'Icon', - description: 'Content type icon.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.BinaryFieldSetting({ - isText: true, - name: 'Binary', - displayName: 'Binary', - description: 'The content type definition in XML format.', - readOnly: false, - compulsory: false, - defaultValue: '\\ MyType\ \ Content\ true\ ContentTypeName1,ContentTypeName2\ \ \ ShortTextField\ \ \ 100\ 0\ [a-zA-Z0-9]*$\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ LongTextField\ \ \ 100\ 0\ LongText|RichText|AdvancedRichText\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ NumberField\ \ \ 0\ 100.5\ 2\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ IntegerField\ \ \ 0\ 100\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ BooleanField\ \ \ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ ChoiceField\ \ \ false\ false\ \ \ \ \ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ DateTimeField\ \ \ DateAndTime\ Second\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ ReferenceField\ \ \ true\ \ Type1\ Type2\ \ \ /Root/Path1\ /Root/Path2\ \ /Root/Path1,/Root/Path2\ false\ false\ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ BinaryField\ \ \ true\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \', - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'CreatedBy', - displayName: 'Created by', - description: 'Content creator.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'CreationDate', - displayName: 'Creation date', - description: 'Content creation date.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'ModifiedBy', - displayName: 'Modified by', - description: 'Content was last modified by the given user.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - name: 'ModificationDate', - displayName: 'Modification date', - description: 'Content was last modified on the given date.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'EnableLifespan', - displayName: 'Enable Lifespan handling', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + export const SchemaStore: Schema[] = [ + { + ContentTypeName: 'ContentType', + DisplayName: '$Ctd-ContentType,DisplayName', + Description: '$Ctd-ContentType,Description', + Icon: 'ContentType', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'IntegerFieldSetting', + Name: 'Id', + DisplayName: 'Id', + Description: 'A unique ID for the Content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'ParentId', + DisplayName: 'Id', + Description: 'A unique ID for the Content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'VersionId', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Name', + DisplayName: 'Uri name', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'CreatedById', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'ModifiedById', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Version', + DisplayName: 'Version', + Description: 'Latest version number.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Path', + DisplayName: 'Path', + Description: 'Content type path.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'Depth', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsSystemContent', + DisplayName: 'System Content', + Description: 'This field is true if content is in a system folder/trash or the content is a system folder/file.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'HandlerName', + DisplayName: 'Handler Name', + Description: 'Fully Qualified Name.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'ParentTypeName', + DisplayName: 'Parent Type Name', + Description: 'Name of the parent content type.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'DisplayName', + DisplayName: 'Name', + Description: 'User friendly name of the content type.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'Description', + DisplayName: 'Description', + Description: 'Longer description of the content type.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Icon', + DisplayName: 'Icon', + Description: 'Content type icon.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'BinaryFieldSetting', + IsText: true, + Name: 'Binary', + DisplayName: 'Binary', + Description: 'The content type definition in XML format.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '\\ MyType\ \ Content\ true\ ContentTypeName1,ContentTypeName2\ \ \ ShortTextField\ \ \ 100\ 0\ [a-zA-Z0-9]*$\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ LongTextField\ \ \ 100\ 0\ LongText|RichText|AdvancedRichText\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ NumberField\ \ \ 0\ 100.5\ 2\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ IntegerField\ \ \ 0\ 100\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ BooleanField\ \ \ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ ChoiceField\ \ \ false\ false\ \ \ \ \ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ DateTimeField\ \ \ DateAndTime\ Second\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ ReferenceField\ \ \ true\ \ Type1\ Type2\ \ \ /Root/Path1\ /Root/Path2\ \ /Root/Path1,/Root/Path2\ false\ false\ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \ BinaryField\ \ \ true\ false\ false\ \ Show|Hide|Advanced\ Show|Hide|Advanced\ Show|Hide|Advanced\ \ \ \', + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.BinaryFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'CreatedBy', + DisplayName: 'Created by', + Description: 'Content creator.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'CreationDate', + DisplayName: 'Creation date', + Description: 'Content creation date.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'ModifiedBy', + DisplayName: 'Modified by', + Description: 'Content was last modified by the given user.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'DateTimeFieldSetting', + Name: 'ModificationDate', + DisplayName: 'Modification date', + Description: 'Content was last modified on the given date.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'EnableLifespan', + DisplayName: 'Enable Lifespan handling', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the GenericContent - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.GenericContent, - DisplayName: '$Ctd-GenericContent,DisplayName', - Description: '$Ctd-GenericContent,Description', - Icon: 'Content', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.IntegerFieldSetting({ - name: 'Id', - displayName: 'Id', - description: 'Unique Id for the content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'ParentId', - displayName: 'Parent Id', - description: 'Id of the parent content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'OwnerId', - displayName: 'Owner Id', - description: 'Id of the owner.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'Owner', - displayName: 'Owner', - description: 'Content owner.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'VersionId', - displayName: 'Version Id', - description: 'Database row Id of current version.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Icon', - displayName: 'Icon', - description: 'Icon', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Name', - displayName: 'Name', - description: 'Specify a name that will appear in the address bar of the browser.', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:Name' - }), - new FieldSettings.IntegerFieldSetting({ - name: 'CreatedById', - displayName: 'Created By (Id)', - description: 'Id of creator.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'ModifiedById', - displayName: 'Last Modified By (Id)', - description: 'Id of last modifier.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Version', - displayName: 'Version', - description: 'Version number.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Path', - displayName: 'Path', - description: 'Content path in the repository.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'Depth', - displayName: 'Tree Depth', - description: 'Content level in the tree. Root level is 0.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsSystemContent', - displayName: 'System Content', - description: 'This field is true if content is in a system folder/trash or the content is a system folder/file.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsFolder', - displayName: 'Folder', - description: 'This field is true if content can contain other content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'DisplayName', - displayName: 'Display Name', - description: 'Content name. You can set any name you prefer without any restrictions.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:DisplayName' - }), - new FieldSettings.LongTextFieldSetting({ - name: 'Description', - displayName: 'Description', - description: 'Description of the content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:RichText' - }), - new FieldSettings.NullFieldSetting({ - name: 'Hidden', - displayName: 'Hidden', - description: 'If checked, content will not show up in navigation.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'Index', - displayName: 'Index', - description: 'Content order in navigation. Numbers closer to 0 will appear first.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'EnableLifespan', - displayName: 'Enable Lifespan', - description: 'Specify whether you want to define starting and end date for the validity of this content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'ValidFrom', - displayName: 'Valid From', - description: 'This content will appear on the date you set if lifespan handling is enabled for this content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'ValidTill', - displayName: 'Valid Till', - description: 'This content will disappear on the date you set if lifespan handling is enabled for this content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'AllowedChildTypes', - displayName: 'Allowed child types', - description: 'You can get and set which content types are explicitly allowed to be created under this content. It is a local setting.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'EffectiveAllowedChildTypes', - displayName: 'Effective allowed child types', - description: 'You can get which content types are effective allowed to be created under this content. If there is no local setting, the global setting takes effect.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, - {Value: '1', Text: 'None', Enabled: true, Selected: false }, - {Value: '2', Text: 'Major only', Enabled: true, Selected: false }, - {Value: '3', Text: 'Major and minor', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'VersioningMode', - displayName: 'Versioning Mode For Current Content', - description: 'It shows the versioning mode of the current content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, - {Value: '1', Text: 'None', Enabled: true, Selected: false }, - {Value: '2', Text: 'Major only', Enabled: true, Selected: false }, - {Value: '3', Text: 'Major and minor', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'InheritableVersioningMode', - displayName: 'Version history', - description: 'Specify whether the system should create a new version whenever you create or modify a content below this content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0, - controlHint: 'sn:VersioningModeChoice' - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'CreatedBy', - displayName: 'Created by', - description: 'Content creator.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'CreationDate', - displayName: 'Creation date', - description: 'Content creation date.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'ModifiedBy', - displayName: 'Modified By', - description: 'Content was last modified by this user.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'ModificationDate', - displayName: 'Modification Date', - description: 'Content was last modified on this date.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, - {Value: '1', Text: 'Off', Enabled: true, Selected: false }, - {Value: '2', Text: 'On', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'ApprovingMode', - displayName: 'Content Approval For Current Content', - description: 'It shows the approval mode of the current content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, - {Value: '1', Text: 'Off', Enabled: true, Selected: false }, - {Value: '2', Text: 'On', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'InheritableApprovingMode', - displayName: 'Content approval', - description: 'Specify whether new or changed content below the current one should remain in a draft state until they have been approved.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0, - controlHint: 'sn:ApprovingModeChoice' - }), - new FieldSettings.NullFieldSetting({ - name: 'Locked', - displayName: 'Locked', - description: 'It shows whether the content is checked out or not.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - name: 'CheckedOutTo', - displayName: 'Checked Out To', - description: 'The user currently locking the content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'TrashDisabled', - displayName: 'Disable Trash', - description: 'You can disable trash for this content and its children. If set, you can not restore deleted content.', - readOnly: false, - compulsory: false, - defaultValue: 'false', - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Finalized', Enabled: true, Selected: false }, - {Value: '1', Text: 'Creating', Enabled: true, Selected: false }, - {Value: '2', Text: 'Modifying', Enabled: true, Selected: false }, - {Value: '3', Text: 'ModifyingLocked', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: 'SenseNet.ContentRepository.Storage.ContentSavingState', - name: 'SavingState', - displayName: 'Saving state', - description: 'State of multi-step saving.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'ExtensionData', - displayName: 'Extension data', - description: 'You can set extra data in this field which is useful when extending a content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - name: 'BrowseApplication', - displayName: 'Reference To Browse Application', - description: 'Set this, if you would like to override the default browse application.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Approvable', - displayName: 'Approvable By Current User', - description: 'This fileld is true if the content is in \'pending\' state and can be approved by the current user.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsTaggable', - displayName: 'Enable Tagging', - description: 'Specify whether you would like to enable tagging capability for this content.', - readOnly: false, - compulsory: false, - defaultValue: 'false', - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'Tags', - displayName: 'Tags', - description: 'List of tags and creators of them separated by commas.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0, - controlHint: 'sn:TagList' - }), - new FieldSettings.NullFieldSetting({ - name: 'IsRateable', - displayName: 'Enable Rating', - description: 'Specify whether you would like to enable rating capability for this content.', - readOnly: false, - compulsory: false, - defaultValue: 'false', - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'RateStr', - displayName: 'Raw value of rating', - description: '', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NumberFieldSetting({ - name: 'RateAvg', - displayName: 'Average rate', - description: 'Average rate of the content.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'RateCount', - displayName: 'Rate count', - description: 'Count of rates.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.RatingFieldSetting({ - range: 5, - split: 1, - name: 'Rate', - displayName: 'Rate', - description: '', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Publishable', - displayName: 'Publishable By Current User', - description: 'This fileld is true if the content can be published by the current user.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - name: 'Versions', - displayName: 'Versions', - description: 'Content version history.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'CheckInComments', - displayName: 'Checkin comments', - description: 'Comments for a new version.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'RejectReason', - displayName: 'Reject reason', - description: 'The reason why the content was rejected.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['Workspace'], - name: 'Workspace', - displayName: 'Workspace', - description: 'The container workspace of the content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'BrowseUrl', - displayName: 'Browse url', - description: '', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'GenericContent', + DisplayName: '$Ctd-GenericContent,DisplayName', + Description: '$Ctd-GenericContent,Description', + Icon: 'Content', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'IntegerFieldSetting', + Name: 'Id', + DisplayName: 'Id', + Description: 'Unique Id for the content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'ParentId', + DisplayName: 'Parent Id', + Description: 'Id of the parent content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'OwnerId', + DisplayName: 'Owner Id', + Description: 'Id of the owner.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'Owner', + DisplayName: 'Owner', + Description: 'Content owner.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'VersionId', + DisplayName: 'Version Id', + Description: 'Database row Id of current version.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Icon', + DisplayName: 'Icon', + Description: 'Icon', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Name', + DisplayName: 'Name', + Description: 'Specify a name that will appear in the address bar of the browser.', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:Name' + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'CreatedById', + DisplayName: 'Created By (Id)', + Description: 'Id of creator.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'ModifiedById', + DisplayName: 'Last Modified By (Id)', + Description: 'Id of last modifier.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Version', + DisplayName: 'Version', + Description: 'Version number.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Path', + DisplayName: 'Path', + Description: 'Content path in the repository.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'Depth', + DisplayName: 'Tree Depth', + Description: 'Content level in the tree. Root level is 0.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsSystemContent', + DisplayName: 'System Content', + Description: 'This field is true if content is in a system folder/trash or the content is a system folder/file.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsFolder', + DisplayName: 'Folder', + Description: 'This field is true if content can contain other content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'DisplayName', + DisplayName: 'Display Name', + Description: 'Content name. You can set any name you prefer without any restrictions.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:DisplayName' + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'Description', + DisplayName: 'Description', + Description: 'Description of the content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:RichText' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Hidden', + DisplayName: 'Hidden', + Description: 'If checked, content will not show up in navigation.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'Index', + DisplayName: 'Index', + Description: 'Content order in navigation. Numbers closer to 0 will appear first.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'EnableLifespan', + DisplayName: 'Enable Lifespan', + Description: 'Specify whether you want to define starting and end date for the validity of this content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'ValidFrom', + DisplayName: 'Valid From', + Description: 'This content will appear on the date you set if lifespan handling is enabled for this content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'ValidTill', + DisplayName: 'Valid Till', + Description: 'This content will disappear on the date you set if lifespan handling is enabled for this content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'AllowedChildTypes', + DisplayName: 'Allowed child types', + Description: 'You can get and set which content types are explicitly allowed to be created under this content. It is a local setting.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'EffectiveAllowedChildTypes', + DisplayName: 'Effective allowed child types', + Description: 'You can get which content types are effective allowed to be created under this content. If there is no local setting, the global setting takes effect.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, + {Value: '1', Text: 'None', Enabled: true, Selected: false }, + {Value: '2', Text: 'Major only', Enabled: true, Selected: false }, + {Value: '3', Text: 'Major and minor', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'VersioningMode', + DisplayName: 'Versioning Mode For Current Content', + Description: 'It shows the versioning mode of the current content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, + {Value: '1', Text: 'None', Enabled: true, Selected: false }, + {Value: '2', Text: 'Major only', Enabled: true, Selected: false }, + {Value: '3', Text: 'Major and minor', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'InheritableVersioningMode', + DisplayName: 'Version history', + Description: 'Specify whether the system should create a new version whenever you create or modify a content below this content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0, + ControlHint: 'sn:VersioningModeChoice' + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'CreatedBy', + DisplayName: 'Created by', + Description: 'Content creator.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'CreationDate', + DisplayName: 'Creation date', + Description: 'Content creation date.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'ModifiedBy', + DisplayName: 'Modified By', + Description: 'Content was last modified by this user.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'ModificationDate', + DisplayName: 'Modification Date', + Description: 'Content was last modified on this date.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, + {Value: '1', Text: 'Off', Enabled: true, Selected: false }, + {Value: '2', Text: 'On', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'ApprovingMode', + DisplayName: 'Content Approval For Current Content', + Description: 'It shows the approval mode of the current content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Inherited', Enabled: true, Selected: true }, + {Value: '1', Text: 'Off', Enabled: true, Selected: false }, + {Value: '2', Text: 'On', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'InheritableApprovingMode', + DisplayName: 'Content approval', + Description: 'Specify whether new or changed content below the current one should remain in a draft state until they have been approved.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0, + ControlHint: 'sn:ApprovingModeChoice' + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Locked', + DisplayName: 'Locked', + Description: 'It shows whether the content is checked out or not.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ReferenceFieldSetting', + Name: 'CheckedOutTo', + DisplayName: 'Checked Out To', + Description: 'The user currently locking the content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'TrashDisabled', + DisplayName: 'Disable Trash', + Description: 'You can disable trash for this content and its children. If set, you can not restore deleted content.', + ReadOnly: false, + Compulsory: false, + DefaultValue: 'false', + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Finalized', Enabled: true, Selected: false }, + {Value: '1', Text: 'Creating', Enabled: true, Selected: false }, + {Value: '2', Text: 'Modifying', Enabled: true, Selected: false }, + {Value: '3', Text: 'ModifyingLocked', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: 'SenseNet.ContentRepository.Storage.ContentSavingState', + Name: 'SavingState', + DisplayName: 'Saving state', + Description: 'State of multi-step saving.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'ExtensionData', + DisplayName: 'Extension data', + Description: 'You can set extra data in this field which is useful when extending a content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + Name: 'BrowseApplication', + DisplayName: 'Reference To Browse Application', + Description: 'Set this, if you would like to override the default browse application.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Approvable', + DisplayName: 'Approvable By Current User', + Description: 'This fileld is true if the content is in \'pending\' state and can be approved by the current user.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsTaggable', + DisplayName: 'Enable Tagging', + Description: 'Specify whether you would like to enable tagging capability for this content.', + ReadOnly: false, + Compulsory: false, + DefaultValue: 'false', + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'Tags', + DisplayName: 'Tags', + Description: 'List of tags and creators of them separated by commas.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0, + ControlHint: 'sn:TagList' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsRateable', + DisplayName: 'Enable Rating', + Description: 'Specify whether you would like to enable rating capability for this content.', + ReadOnly: false, + Compulsory: false, + DefaultValue: 'false', + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'RateStr', + DisplayName: 'Raw value of rating', + Description: '', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'NumberFieldSetting', + Name: 'RateAvg', + DisplayName: 'Average rate', + Description: 'Average rate of the content.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NumberFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'RateCount', + DisplayName: 'Rate count', + Description: 'Count of rates.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'RatingFieldSetting', + Range: 5, + Split: 1, + Name: 'Rate', + DisplayName: 'Rate', + Description: '', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.RatingFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Publishable', + DisplayName: 'Publishable By Current User', + Description: 'This fileld is true if the content can be published by the current user.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + Name: 'Versions', + DisplayName: 'Versions', + Description: 'Content version history.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'CheckInComments', + DisplayName: 'Checkin comments', + Description: 'Comments for a new version.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'RejectReason', + DisplayName: 'Reject reason', + Description: 'The reason why the content was rejected.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['Workspace'], + Name: 'Workspace', + DisplayName: 'Workspace', + Description: 'The container workspace of the content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'BrowseUrl', + DisplayName: 'Browse url', + Description: '', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the ContentLink - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ContentLink, - DisplayName: '$Ctd-ContentLink,DisplayName', - Description: '$Ctd-ContentLink,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - name: 'Link', - displayName: 'Linked content', - description: 'Set this reference to the Content to link.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'ContentLink', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-ContentLink,DisplayName', + Description: '$Ctd-ContentLink,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + Name: 'Link', + DisplayName: 'Linked content', + Description: 'Set this reference to the Content to link.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the File - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.File, - DisplayName: '$Ctd-File,DisplayName', - Description: '$Ctd-File,Description', - Icon: 'File', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.BinaryFieldSetting({ - name: 'Binary', - displayName: 'Binary', - description: 'The binary content of the document.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NumberFieldSetting({ - name: 'Size', - displayName: 'Size', - description: 'Size of the binary document.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NumberFieldSetting({ - name: 'FullSize', - displayName: 'Full size', - description: 'The total amount of space the Document occupies, counting all versions.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'PageCount', - displayName: 'Page count', - description: 'Read-only field for storing the number of pages in the document. It is filled by the document preview generator.', - readOnly: false, - compulsory: false, - defaultValue: '-4', - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'MimeType', - displayName: 'Document MIME type', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'Shapes', - displayName: 'Shapes', - description: 'Stores data used for document preview (redaction, highlight, annotation shapes). This value can be modified by the document preview plugin.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - name: 'PageAttributes', - displayName: 'Page attributes', - description: 'Stores data used for document preview (for example page rotation). This value can be modified by the document preview plugin.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Watermark', - displayName: 'Watermark', - description: 'The text that is displayed as a watermark on the document preview. The format can be set by modifying the Document Preview settings.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'File', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-File,DisplayName', + Description: '$Ctd-File,Description', + Icon: 'File', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'BinaryFieldSetting', + Name: 'Binary', + DisplayName: 'Binary', + Description: 'The binary content of the document.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.BinaryFieldSetting, + { + Type: 'NumberFieldSetting', + Name: 'Size', + DisplayName: 'Size', + Description: 'Size of the binary document.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NumberFieldSetting, + { + Type: 'NumberFieldSetting', + Name: 'FullSize', + DisplayName: 'Full size', + Description: 'The total amount of space the Document occupies, counting all versions.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NumberFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'PageCount', + DisplayName: 'Page count', + Description: 'Read-only field for storing the number of pages in the document. It is filled by the document preview generator.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '-4', + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'MimeType', + DisplayName: 'Document MIME type', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'Shapes', + DisplayName: 'Shapes', + Description: 'Stores data used for document preview (redaction, highlight, annotation shapes). This value can be modified by the document preview plugin.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'LongTextFieldSetting', + Name: 'PageAttributes', + DisplayName: 'Page attributes', + Description: 'Stores data used for document preview (for example page rotation). This value can be modified by the document preview plugin.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Watermark', + DisplayName: 'Watermark', + Description: 'The text that is displayed as a watermark on the document preview. The format can be set by modifying the Document Preview settings.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the DynamicJsonContent - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.DynamicJsonContent, - DisplayName: 'Dynamic JSON content', - Description: '', - Icon: 'Settings', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'DynamicJsonContent', + ParentTypeName: 'File', + DisplayName: 'Dynamic JSON content', + Description: '', + Icon: 'Settings', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the ExecutableFile - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ExecutableFile, - DisplayName: '$Ctd-ExecutableFile,DisplayName', - Description: '$Ctd-ExecutableFile,Description', - Icon: 'Application', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'ExecutableFile', + ParentTypeName: 'File', + DisplayName: '$Ctd-ExecutableFile,DisplayName', + Description: '$Ctd-ExecutableFile,Description', + Icon: 'Application', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the HtmlTemplate - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.HtmlTemplate, - DisplayName: '$Ctd-HtmlTemplate,DisplayName', - Description: '$Ctd-HtmlTemplate,Description', - Icon: 'File', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'TemplateText', - displayName: 'Template text', - description: 'Shows the contents of the html file as a text.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'HtmlTemplate', + ParentTypeName: 'File', + DisplayName: '$Ctd-HtmlTemplate,DisplayName', + Description: '$Ctd-HtmlTemplate,Description', + Icon: 'File', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'TemplateText', + DisplayName: 'Template text', + Description: 'Shows the contents of the html file as a text.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Image - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Image, - DisplayName: '$Ctd-Image,DisplayName', - Description: '$Ctd-Image,Description', - Icon: 'Image', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'Keywords', - displayName: 'Keywords', - description: 'Keywords describing the image.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'DateTaken', - displayName: 'Date taken', - description: 'Date the photo was taken, if applicable.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'Width', - displayName: 'Width', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'Height', - displayName: 'Height', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Image', + ParentTypeName: 'File', + DisplayName: '$Ctd-Image,DisplayName', + Description: '$Ctd-Image,Description', + Icon: 'Image', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'Keywords', + DisplayName: 'Keywords', + Description: 'Keywords describing the image.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'DateTaken', + DisplayName: 'Date taken', + Description: 'Date the photo was taken, if applicable.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'Width', + DisplayName: 'Width', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'Height', + DisplayName: 'Height', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the PreviewImage - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.PreviewImage, - DisplayName: '$Ctd-PreviewImage,DisplayName', - Description: '$Ctd-PreviewImage,Description', - Icon: 'Image', - AllowIndexing: false, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'PreviewImage', + ParentTypeName: 'Image', + DisplayName: '$Ctd-PreviewImage,DisplayName', + Description: '$Ctd-PreviewImage,Description', + Icon: 'Image', + AllowIndexing: false, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Settings - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Settings, - DisplayName: '$Ctd-Settings,DisplayName', - Description: '$Ctd-Settings,Description', - Icon: 'Settings', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.NullFieldSetting({ - name: 'GlobalOnly', - displayName: 'Global only', - description: 'Switching this ON will prevent the creation of local settings with the same name preventing others to gain access to the contents of this settings file through inheritance.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Settings', + ParentTypeName: 'File', + DisplayName: '$Ctd-Settings,DisplayName', + Description: '$Ctd-Settings,Description', + Icon: 'Settings', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'NullFieldSetting', + Name: 'GlobalOnly', + DisplayName: 'Global only', + Description: 'Switching this ON will prevent the creation of local settings with the same name preventing others to gain access to the contents of this settings file through inheritance.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the IndexingSettings - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.IndexingSettings, - DisplayName: '$Ctd-IndexingSettings,DisplayName', - Description: '$Ctd-IndexingSettings,Description', - Icon: 'Settings', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.NullFieldSetting({ - name: 'TextExtractorInstances', - displayName: 'Text extractor instances', - description: 'Dynamically generated text extractor instance collection.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'IndexingSettings', + ParentTypeName: 'Settings', + DisplayName: '$Ctd-IndexingSettings,DisplayName', + Description: '$Ctd-IndexingSettings,Description', + Icon: 'Settings', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'NullFieldSetting', + Name: 'TextExtractorInstances', + DisplayName: 'Text extractor instances', + Description: 'Dynamically generated text extractor instance collection.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the LoggingSettings - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.LoggingSettings, - DisplayName: '$Ctd-LoggingSettings,DisplayName', - Description: '$Ctd-LoggingSettings,Description', - Icon: 'Settings', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'LoggingSettings', + ParentTypeName: 'Settings', + DisplayName: '$Ctd-LoggingSettings,DisplayName', + Description: '$Ctd-LoggingSettings,Description', + Icon: 'Settings', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the PortalSettings - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.PortalSettings, - DisplayName: '$Ctd-PortalSettings,DisplayName', - Description: '$Ctd-PortalSettings,Description', - Icon: 'Settings', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'PortalSettings', + ParentTypeName: 'Settings', + DisplayName: '$Ctd-PortalSettings,DisplayName', + Description: '$Ctd-PortalSettings,Description', + Icon: 'Settings', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the SystemFile - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.SystemFile, - DisplayName: '$Ctd-SystemFile,DisplayName', - Description: '$Ctd-SystemFile,Description', - Icon: 'File', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'SystemFile', + ParentTypeName: 'File', + DisplayName: '$Ctd-SystemFile,DisplayName', + Description: '$Ctd-SystemFile,Description', + Icon: 'File', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Resource - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Resource, - DisplayName: '$Ctd-Resource,DisplayName', - Description: '$Ctd-Resource,Description', - Icon: 'Resource', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.NumberFieldSetting({ - name: 'Downloads', - displayName: 'Downloads', - description: 'The number of downloads.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Resource', + ParentTypeName: 'SystemFile', + DisplayName: '$Ctd-Resource,DisplayName', + Description: '$Ctd-Resource,Description', + Icon: 'Resource', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'NumberFieldSetting', + Name: 'Downloads', + DisplayName: 'Downloads', + Description: 'The number of downloads.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NumberFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Folder - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Folder, - DisplayName: '$Ctd-Folder,DisplayName', - Description: '$Ctd-Folder,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Folder', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-Folder,DisplayName', + Description: '$Ctd-Folder,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the ContentList - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ContentList, - DisplayName: '$Ctd-ContentList,DisplayName', - Description: '$Ctd-ContentList,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'ContentListDefinition', - displayName: 'List Definition', - description: 'XML definition for additional fields.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'DefaultView', - displayName: 'Default view', - description: 'The default View to use.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['ListView'], - name: 'AvailableViews', - displayName: 'Available views', - description: 'Select global content list views here that you want to offer users to choose from.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['FieldSettingContent'], - name: 'FieldSettingContents', - displayName: 'FieldSetting content', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['FieldSettingContent'], - name: 'AvailableContentTypeFields', - displayName: 'Available ContentType Field content.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'ListEmail', - displayName: 'Email address of Content List', - description: 'Emails sent to this address will be imported as Email content into the Document Library.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'ExchangeSubscriptionId', - displayName: 'Exchange Subscription Id', - description: 'Ctd-ContentListen-USExchangeSubscriptionId-Descriptione', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'OverwriteFiles', - displayName: 'Overwrite files with same name', - description: 'If checked new emails and attachments with the same name will overwrite existing items in list. Otherwise increment suffix is used in the name of new mail items.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: 'email', Text: 'Save all attachments as children of separate Email content', Enabled: true, Selected: true }, - {Value: 'root', Text: 'Save all attachments in root', Enabled: true, Selected: false }, - {Value: 'subject', Text: 'Save all attachments in folders grouped by subject', Enabled: true, Selected: false }, - {Value: 'sender', Text: 'Save all attachments in folders grouped by sender', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'GroupAttachments', - displayName: 'Group attachments', - description: 'Select the appropriate option to group attachment files under folders or email content or not.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'SaveOriginalEmail', - displayName: 'Save original email', - description: 'A separate .eml file will be created for every incoming email.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - selectionRoots: ['/Root/System/Schema/ContentTypes/GenericContent/Workflow/MailProcessorWorkflow'], - name: 'IncomingEmailWorkflow', - displayName: 'Incoming email workflow', - description: 'Select the workflow to be executed on every incoming email.', - readOnly: false, - compulsory: false, - defaultValue: '/Root/System/Schema/ContentTypes/GenericContent/Workflow/MailProcessorWorkflow', - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'OnlyFromLocalGroups', - displayName: 'Accept e-mails only from users in local groups', - description: 'If set, only users that are members of any local group are able to send e-mails to this library.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'InboxFolder', - displayName: 'Inbox folder', - description: 'A relative path of a folder to store incoming e-mails.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['User'], - selectionRoots: ['/Root/IMS'], - name: 'OwnerWhenVisitor', - displayName: 'Owner of items created by visitor', - description: 'If a Visitor adds content to this list, this user will be set as the creator instead of the Visitor. This prevents visitors see each others\' content.', - readOnly: false, - compulsory: false, - defaultValue: '/Root/IMS/BuiltIn/Portal/Admin', - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'ContentList', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-ContentList,DisplayName', + Description: '$Ctd-ContentList,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'ContentListDefinition', + DisplayName: 'List Definition', + Description: 'XML definition for additional fields.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'DefaultView', + DisplayName: 'Default view', + Description: 'The default View to use.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['ListView'], + Name: 'AvailableViews', + DisplayName: 'Available views', + Description: 'Select global content list views here that you want to offer users to choose from.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['FieldSettingContent'], + Name: 'FieldSettingContents', + DisplayName: 'FieldSetting content', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['FieldSettingContent'], + Name: 'AvailableContentTypeFields', + DisplayName: 'Available ContentType Field content.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'ListEmail', + DisplayName: 'Email address of Content List', + Description: 'Emails sent to this address will be imported as Email content into the Document Library.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'ExchangeSubscriptionId', + DisplayName: 'Exchange Subscription Id', + Description: 'Ctd-ContentListen-USExchangeSubscriptionId-Descriptione', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'OverwriteFiles', + DisplayName: 'Overwrite files with same name', + Description: 'If checked new emails and attachments with the same name will overwrite existing items in list. Otherwise increment suffix is used in the name of new mail items.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: 'email', Text: 'Save all attachments as children of separate Email content', Enabled: true, Selected: true }, + {Value: 'root', Text: 'Save all attachments in root', Enabled: true, Selected: false }, + {Value: 'subject', Text: 'Save all attachments in folders grouped by subject', Enabled: true, Selected: false }, + {Value: 'sender', Text: 'Save all attachments in folders grouped by sender', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'GroupAttachments', + DisplayName: 'Group attachments', + Description: 'Select the appropriate option to group attachment files under folders or email content or not.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'SaveOriginalEmail', + DisplayName: 'Save original email', + Description: 'A separate .eml file will be created for every incoming email.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + SelectionRoots: ['/Root/System/Schema/ContentTypes/GenericContent/Workflow/MailProcessorWorkflow'], + Name: 'IncomingEmailWorkflow', + DisplayName: 'Incoming email workflow', + Description: 'Select the workflow to be executed on every incoming email.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '/Root/System/Schema/ContentTypes/GenericContent/Workflow/MailProcessorWorkflow', + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'OnlyFromLocalGroups', + DisplayName: 'Accept e-mails only from users in local groups', + Description: 'If set, only users that are members of any local group are able to send e-mails to this library.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'InboxFolder', + DisplayName: 'Inbox folder', + Description: 'A relative path of a folder to store incoming e-mails.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['User'], + SelectionRoots: ['/Root/IMS'], + Name: 'OwnerWhenVisitor', + DisplayName: 'Owner of items created by visitor', + Description: 'If a Visitor adds content to this list, this user will be set as the creator instead of the Visitor. This prevents visitors see each others\' content.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '/Root/IMS/BuiltIn/Portal/Admin', + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Aspect - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Aspect, - DisplayName: '$Ctd-Aspect,DisplayName', - Description: '$Ctd-Aspect,Description', - Icon: 'Aspect', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'AspectDefinition', - displayName: 'Aspect definition', - description: 'Definition of the extension in XML format.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Aspect', + ParentTypeName: 'ContentList', + DisplayName: '$Ctd-Aspect,DisplayName', + Description: '$Ctd-Aspect,Description', + Icon: 'Aspect', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'AspectDefinition', + DisplayName: 'Aspect definition', + Description: 'Definition of the extension in XML format.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.LongTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the ItemList - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ItemList, - DisplayName: '$Ctd-ItemList,DisplayName', - Description: '$Ctd-ItemList,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'ItemList', + ParentTypeName: 'ContentList', + DisplayName: '$Ctd-ItemList,DisplayName', + Description: '$Ctd-ItemList,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the CustomList - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.CustomList, - DisplayName: '$Ctd-CustomList,DisplayName', - Description: '$Ctd-CustomList,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['ListItem'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'CustomList', + ParentTypeName: 'ItemList', + DisplayName: '$Ctd-CustomList,DisplayName', + Description: '$Ctd-CustomList,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['ListItem'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the MemoList - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.MemoList, - DisplayName: '$Ctd-MemoList,DisplayName', - Description: '$Ctd-MemoList,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Memo'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'MemoList', + ParentTypeName: 'ItemList', + DisplayName: '$Ctd-MemoList,DisplayName', + Description: '$Ctd-MemoList,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Memo'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the TaskList - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.TaskList, - DisplayName: '$Ctd-TaskList,DisplayName', - Description: '$Ctd-TaskList,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Task'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'TaskList', + ParentTypeName: 'ItemList', + DisplayName: '$Ctd-TaskList,DisplayName', + Description: '$Ctd-TaskList,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Task'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Library - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Library, - DisplayName: '$Ctd-Library,DisplayName', - Description: '$Ctd-Library,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Library', + ParentTypeName: 'ContentList', + DisplayName: '$Ctd-Library,DisplayName', + Description: '$Ctd-Library,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the DocumentLibrary - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.DocumentLibrary, - DisplayName: '$Ctd-DocumentLibrary,DisplayName', - Description: '$Ctd-DocumentLibrary,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Folder', 'File'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'DocumentLibrary', + ParentTypeName: 'Library', + DisplayName: '$Ctd-DocumentLibrary,DisplayName', + Description: '$Ctd-DocumentLibrary,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Folder', 'File'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the ImageLibrary - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ImageLibrary, - DisplayName: '$Ctd-ImageLibrary,DisplayName', - Description: '$Ctd-ImageLibrary,Description', - Icon: 'ContentList', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Folder', 'Image'], - FieldSettings: [ - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['Image'], - name: 'CoverImage', - displayName: 'Cover image', - description: 'Select cover image', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'ImageLibrary', + ParentTypeName: 'Library', + DisplayName: '$Ctd-ImageLibrary,DisplayName', + Description: '$Ctd-ImageLibrary,Description', + Icon: 'ContentList', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Folder', 'Image'], + FieldSettings: [ + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['Image'], + Name: 'CoverImage', + DisplayName: 'Cover image', + Description: 'Select cover image', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Device - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Device, - DisplayName: '$Ctd-Device,DisplayName', - Description: '$Ctd-Device,Description', - Icon: 'Device', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.ShortTextFieldSetting({ - name: 'UserAgentPattern', - displayName: 'User agent string', - description: 'A regular expression to match the user agent string of the browser.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Device', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Device,DisplayName', + Description: '$Ctd-Device,Description', + Icon: 'Device', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'ShortTextFieldSetting', + Name: 'UserAgentPattern', + DisplayName: 'User agent string', + Description: 'A regular expression to match the user agent string of the browser.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Domain - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Domain, - DisplayName: '$Ctd-Domain,DisplayName', - Description: '$Ctd-Domain,Description', - Icon: 'Domain', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['User', 'Group', 'OrganizationalUnit'], - FieldSettings: [ - new FieldSettings.ShortTextFieldSetting({ - name: 'SyncGuid', - displayName: 'SyncGuid', - description: 'GUID of corresponding AD object.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'LastSync', - displayName: 'LastSync', - description: 'Date of last synchronization.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Domain', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Domain,DisplayName', + Description: '$Ctd-Domain,Description', + Icon: 'Domain', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['User', 'Group', 'OrganizationalUnit'], + FieldSettings: [ + { + Type: 'ShortTextFieldSetting', + Name: 'SyncGuid', + DisplayName: 'SyncGuid', + Description: 'GUID of corresponding AD object.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'LastSync', + DisplayName: 'LastSync', + Description: 'Date of last synchronization.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Domains - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Domains, - DisplayName: '$Ctd-Domains,DisplayName', - Description: '$Ctd-Domains,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Domain'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Domains', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Domains,DisplayName', + Description: '$Ctd-Domains,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Domain'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Email - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Email, - DisplayName: '$Ctd-Email,DisplayName', - Description: '$Ctd-Email,Description', - Icon: 'Document', - AllowIndexing: true, - AllowIncrementalNaming: true, - AllowedChildTypes: ['File'], - FieldSettings: [ - new FieldSettings.ShortTextFieldSetting({ - name: 'From', - displayName: 'From', - description: 'Sender name and address.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - textType: FieldSettings.TextType.RichText, - name: 'Body', - displayName: 'Body', - description: 'Body of email.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:RichText' - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'Sent', - displayName: 'Sent', - description: 'Date of sending.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Email', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Email,DisplayName', + Description: '$Ctd-Email,Description', + Icon: 'Document', + AllowIndexing: true, + AllowIncrementalNaming: true, + AllowedChildTypes: ['File'], + FieldSettings: [ + { + Type: 'ShortTextFieldSetting', + Name: 'From', + DisplayName: 'From', + Description: 'Sender name and address.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'LongTextFieldSetting', + TextType: FieldSettings.TextType.RichText, + Name: 'Body', + DisplayName: 'Body', + Description: 'Body of email.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:RichText' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'Sent', + DisplayName: 'Sent', + Description: 'Date of sending.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the OrganizationalUnit - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.OrganizationalUnit, - DisplayName: '$Ctd-OrganizationalUnit,DisplayName', - Description: '$Ctd-OrganizationalUnit,Description', - Icon: 'OrgUnit', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['User', 'Group', 'OrganizationalUnit'], - FieldSettings: [ - new FieldSettings.ShortTextFieldSetting({ - name: 'SyncGuid', - displayName: 'SyncGuid', - description: 'GUID of corresponding AD object.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'LastSync', - displayName: 'LastSync', - description: 'Date of last synchronization.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'OrganizationalUnit', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-OrganizationalUnit,DisplayName', + Description: '$Ctd-OrganizationalUnit,Description', + Icon: 'OrgUnit', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['User', 'Group', 'OrganizationalUnit'], + FieldSettings: [ + { + Type: 'ShortTextFieldSetting', + Name: 'SyncGuid', + DisplayName: 'SyncGuid', + Description: 'GUID of corresponding AD object.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'LastSync', + DisplayName: 'LastSync', + Description: 'Date of last synchronization.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the PortalRoot - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.PortalRoot, - DisplayName: '$Ctd-PortalRoot,DisplayName', - Description: '$Ctd-PortalRoot,Description', - Icon: 'PortalRoot', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Folder', 'SystemFolder', 'TrashBin', 'ContentList', 'CustomList', 'Sites', 'Domains', 'Profiles', 'Resources', 'Workspace'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'PortalRoot', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-PortalRoot,DisplayName', + Description: '$Ctd-PortalRoot,Description', + Icon: 'PortalRoot', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Folder', 'SystemFolder', 'TrashBin', 'ContentList', 'CustomList', 'Sites', 'Domains', 'Profiles', 'Resources', 'Workspace'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the ProfileDomain - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ProfileDomain, - DisplayName: '$Ctd-ProfileDomain,DisplayName', - Description: '$Ctd-ProfileDomain,Description', - Icon: 'Domain', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['UserProfile'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'ProfileDomain', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-ProfileDomain,DisplayName', + Description: '$Ctd-ProfileDomain,Description', + Icon: 'Domain', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['UserProfile'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Profiles - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Profiles, - DisplayName: '$Ctd-Profiles,DisplayName', - Description: '$Ctd-Profiles,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['ProfileDomain'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Profiles', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Profiles,DisplayName', + Description: '$Ctd-Profiles,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['ProfileDomain'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the RuntimeContentContainer - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.RuntimeContentContainer, - DisplayName: '$Ctd-RuntimeContentContainer,DisplayName', - Description: '$Ctd-RuntimeContentContainer,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'RuntimeContentContainer', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-RuntimeContentContainer,DisplayName', + Description: '$Ctd-RuntimeContentContainer,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Sites - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Sites, - DisplayName: '$Ctd-Sites,DisplayName', - Description: '$Ctd-Sites,Description', - Icon: 'Site', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Site'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Sites', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Sites,DisplayName', + Description: '$Ctd-Sites,Description', + Icon: 'Site', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Site'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the SmartFolder - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.SmartFolder, - DisplayName: '$Ctd-SmartFolder,DisplayName', - Description: '$Ctd-SmartFolder,Description', - Icon: 'SmartFolder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'Query', - displayName: 'Query', - description: 'Please give a query here that you want to use for collecting the children of this smart folder.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:QueryBuilder' - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Default', Enabled: true, Selected: false }, - {Value: '1', Text: 'Enabled', Enabled: true, Selected: false }, - {Value: '2', Text: 'Disabled', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: 'SenseNet.Search.FilterStatus', - name: 'EnableAutofilters', - displayName: 'Enable autofilters', - description: 'If autofilters are enabled, system content will be filtered from the query.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '0', Text: 'Default', Enabled: true, Selected: false }, - {Value: '1', Text: 'Enabled', Enabled: true, Selected: false }, - {Value: '2', Text: 'Disabled', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: 'SenseNet.Search.FilterStatus', - name: 'EnableLifespanFilter', - displayName: 'Enable lifespan filter', - description: 'If lifespan filter is enabled, only valid content will be in the result.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'SmartFolder', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-SmartFolder,DisplayName', + Description: '$Ctd-SmartFolder,Description', + Icon: 'SmartFolder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'Query', + DisplayName: 'Query', + Description: 'Please give a query here that you want to use for collecting the children of this smart folder.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:QueryBuilder' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Default', Enabled: true, Selected: false }, + {Value: '1', Text: 'Enabled', Enabled: true, Selected: false }, + {Value: '2', Text: 'Disabled', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: 'SenseNet.Search.FilterStatus', + Name: 'EnableAutofilters', + DisplayName: 'Enable autofilters', + Description: 'If autofilters are enabled, system content will be filtered from the query.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '0', Text: 'Default', Enabled: true, Selected: false }, + {Value: '1', Text: 'Enabled', Enabled: true, Selected: false }, + {Value: '2', Text: 'Disabled', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: 'SenseNet.Search.FilterStatus', + Name: 'EnableLifespanFilter', + DisplayName: 'Enable lifespan filter', + Description: 'If lifespan filter is enabled, only valid content will be in the result.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the SystemFolder - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.SystemFolder, - DisplayName: '$Ctd-SystemFolder,DisplayName', - Description: '$Ctd-SystemFolder,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'SystemFolder', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-SystemFolder,DisplayName', + Description: '$Ctd-SystemFolder,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the Resources - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Resources, - DisplayName: '$Ctd-Resources,DisplayName', - Description: '$Ctd-Resources,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Resource'], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'Resources', + ParentTypeName: 'SystemFolder', + DisplayName: '$Ctd-Resources,DisplayName', + Description: '$Ctd-Resources,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Resource'], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the TrashBag - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.TrashBag, - DisplayName: '$Ctd-TrashBag,DisplayName', - Description: '$Ctd-TrashBag,Description', - Icon: 'Folder', - AllowIndexing: true, - AllowIncrementalNaming: true, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.DateTimeFieldSetting({ - name: 'KeepUntil', - displayName: 'Keep until', - description: 'The bag must be kept until this date.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'OriginalPath', - displayName: 'Original path', - description: 'The path where the bag content were deleted from.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'WorkspaceRelativePath', - displayName: 'Ctd-TrashBagen-USWorkspaceRelativePath-DisplayName', - description: 'Ctd-TrashBagen-USWorkspaceRelativePath-Description', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'WorkspaceId', - displayName: 'Ctd-TrashBagen-USWorkspaceId-DisplayName', - description: 'Ctd-TrashBagen-USWorkspaceId-Description', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - name: 'DeletedContent', - displayName: 'Deleted content', - description: 'The actual deleted content inside this trash bag.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'TrashBag', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-TrashBag,DisplayName', + Description: '$Ctd-TrashBag,Description', + Icon: 'Folder', + AllowIndexing: true, + AllowIncrementalNaming: true, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'DateTimeFieldSetting', + Name: 'KeepUntil', + DisplayName: 'Keep until', + Description: 'The bag must be kept until this date.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'OriginalPath', + DisplayName: 'Original path', + Description: 'The path where the bag content were deleted from.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'WorkspaceRelativePath', + DisplayName: 'Ctd-TrashBagen-USWorkspaceRelativePath-DisplayName', + Description: 'Ctd-TrashBagen-USWorkspaceRelativePath-Description', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'WorkspaceId', + DisplayName: 'Ctd-TrashBagen-USWorkspaceId-DisplayName', + Description: 'Ctd-TrashBagen-USWorkspaceId-Description', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + Name: 'DeletedContent', + DisplayName: 'Deleted content', + Description: 'The actual deleted content inside this trash bag.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Workspace - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Workspace, - DisplayName: '$Ctd-Workspace,DisplayName', - Description: '$Ctd-Workspace,Description', - Icon: 'Workspace', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Folder', 'DocumentLibrary', 'ImageLibrary', 'MemoList', 'TaskList', 'CustomList', 'Workspace'], - FieldSettings: [ - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['User'], - selectionRoots: ['/Root/IMS', '/Root'], - name: 'Manager', - displayName: 'Project manager', - description: 'The person responsible for the project.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'Deadline', - displayName: 'Project deadline', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsActive', - displayName: 'Active', - description: 'This workspace is currently active.', - readOnly: false, - compulsory: true, - defaultValue: 'true', - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['Skin'], - selectionRoots: ['/Root/Skins'], - name: 'WorkspaceSkin', - displayName: 'Skin', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsCritical', - displayName: 'Is critical', - description: 'This workspace is currently in a critical status.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsWallContainer', - displayName: 'Wall Container', - description: 'This workspace is configured to contain a wall - this indicates that posts are created under this workspace if Content are shared anywhere below this path.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'IsFollowed', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Workspace', + ParentTypeName: 'Folder', + DisplayName: '$Ctd-Workspace,DisplayName', + Description: '$Ctd-Workspace,Description', + Icon: 'Workspace', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Folder', 'DocumentLibrary', 'ImageLibrary', 'MemoList', 'TaskList', 'CustomList', 'Workspace'], + FieldSettings: [ + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['User'], + SelectionRoots: ['/Root/IMS', '/Root'], + Name: 'Manager', + DisplayName: 'Project manager', + Description: 'The person responsible for the project.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'Deadline', + DisplayName: 'Project deadline', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsActive', + DisplayName: 'Active', + Description: 'This workspace is currently active.', + ReadOnly: false, + Compulsory: true, + DefaultValue: 'true', + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['Skin'], + SelectionRoots: ['/Root/Skins'], + Name: 'WorkspaceSkin', + DisplayName: 'Skin', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsCritical', + DisplayName: 'Is critical', + Description: 'This workspace is currently in a critical status.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsWallContainer', + DisplayName: 'Wall Container', + Description: 'This workspace is configured to contain a wall - this indicates that posts are created under this workspace if Content are shared anywhere below this path.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'IsFollowed', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Site - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Site, - DisplayName: '$Ctd-Site,DisplayName', - Description: '$Ctd-Site,Description', - Icon: 'Site', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Folder', 'Workspace', 'Profiles', 'Image', 'DocumentLibrary', 'ImageLibrary', 'MemoList', 'TaskList', 'CustomList', 'SmartFolder'], - FieldSettings: [ - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: 'en', Text: 'English', Enabled: true, Selected: true }, - {Value: 'hu', Text: 'Hungarian', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'Language', - displayName: 'Language', - description: 'Please define the default language of this site.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'EnableClientBasedCulture', - displayName: 'Enable client-based culture', - description: 'Enable this to allow user browser settings override default site language settings.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'EnableUserBasedCulture', - displayName: 'Enable user-based culture', - description: 'Enable this to allow user language settings override default site language settings.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'UrlList', - displayName: 'URL list', - description: 'Select the URLs to associate with this Site.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - selectionRoots: ['.'], - name: 'StartPage', - displayName: 'Alternative start page', - description: 'If set, the site will use this start page instead of the default.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - selectionRoots: ['.'], - name: 'LoginPage', - displayName: 'Login page', - description: 'The login page to display when a user tries to access restricted Content (Forms authentication only).', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['Skin'], - selectionRoots: ['/Root/Skins'], - name: 'SiteSkin', - displayName: 'Skin', - description: '', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'DenyCrossSiteAccess', - displayName: 'Deny cross-site access', - description: 'If set, content under this site can only be accessed via this site and not via other sites using a Root relative path.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Site', + ParentTypeName: 'Workspace', + DisplayName: '$Ctd-Site,DisplayName', + Description: '$Ctd-Site,Description', + Icon: 'Site', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Folder', 'Workspace', 'Profiles', 'Image', 'DocumentLibrary', 'ImageLibrary', 'MemoList', 'TaskList', 'CustomList', 'SmartFolder'], + FieldSettings: [ + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: 'en', Text: 'English', Enabled: true, Selected: true }, + {Value: 'hu', Text: 'Hungarian', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'Language', + DisplayName: 'Language', + Description: 'Please define the default language of this site.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'EnableClientBasedCulture', + DisplayName: 'Enable client-based culture', + Description: 'Enable this to allow user browser settings override default site language settings.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'EnableUserBasedCulture', + DisplayName: 'Enable user-based culture', + Description: 'Enable this to allow user language settings override default site language settings.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'UrlList', + DisplayName: 'URL list', + Description: 'Select the URLs to associate with this Site.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + SelectionRoots: ['.'], + Name: 'StartPage', + DisplayName: 'Alternative start page', + Description: 'If set, the site will use this start page instead of the default.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + SelectionRoots: ['.'], + Name: 'LoginPage', + DisplayName: 'Login page', + Description: 'The login page to display when a user tries to access restricted Content (Forms authentication only).', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['Skin'], + SelectionRoots: ['/Root/Skins'], + Name: 'SiteSkin', + DisplayName: 'Skin', + Description: '', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'DenyCrossSiteAccess', + DisplayName: 'Deny cross-site access', + Description: 'If set, content under this site can only be accessed via this site and not via other sites using a Root relative path.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the TrashBin - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.TrashBin, - DisplayName: '$Ctd-TrashBin,DisplayName', - Description: '$Ctd-TrashBin,Description', - Icon: 'trash', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['TrashBag'], - FieldSettings: [ - new FieldSettings.IntegerFieldSetting({ - minValue: 0, - name: 'MinRetentionTime', - displayName: 'Minimum retention time (in days)', - description: '', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - minValue: 0, - name: 'SizeQuota', - displayName: 'Size quota', - description: 'Set the size quote for the trash bin (Megabytes).', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - minValue: 0, - name: 'BagCapacity', - displayName: 'Trashbag capacity', - description: 'The maximum number of nodes accepted for trash in a single operation.', - readOnly: false, - compulsory: false, - defaultValue: '100', - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'TrashBin', + ParentTypeName: 'Workspace', + DisplayName: '$Ctd-TrashBin,DisplayName', + Description: '$Ctd-TrashBin,Description', + Icon: 'trash', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['TrashBag'], + FieldSettings: [ + { + Type: 'IntegerFieldSetting', + MinValue: 0, + Name: 'MinRetentionTime', + DisplayName: 'Minimum retention time (in days)', + Description: '', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + MinValue: 0, + Name: 'SizeQuota', + DisplayName: 'Size quota', + Description: 'Set the size quote for the trash bin (Megabytes).', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + MinValue: 0, + Name: 'BagCapacity', + DisplayName: 'Trashbag capacity', + Description: 'The maximum number of nodes accepted for trash in a single operation.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '100', + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the UserProfile - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.UserProfile, - DisplayName: '$Ctd-UserProfile,DisplayName', - Description: '$Ctd-UserProfile,Description', - Icon: 'UserProfile', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['DocumentLibrary', 'MemoList', 'TaskList', 'ImageLibrary', 'CustomList'], - FieldSettings: [ - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['User'], - selectionRoots: ['/Root/IMS'], - name: 'User', - displayName: 'User', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'UserProfile', + ParentTypeName: 'Workspace', + DisplayName: '$Ctd-UserProfile,DisplayName', + Description: '$Ctd-UserProfile,Description', + Icon: 'UserProfile', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['DocumentLibrary', 'MemoList', 'TaskList', 'ImageLibrary', 'CustomList'], + FieldSettings: [ + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['User'], + SelectionRoots: ['/Root/IMS'], + Name: 'User', + DisplayName: 'User', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Group - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Group, - DisplayName: '$Ctd-Group,DisplayName', - Description: '$Ctd-Group,Description', - Icon: 'Group', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['User', 'Group'], - selectionRoots: ['/Root/IMS', '/Root'], - name: 'Members', - displayName: 'Members', - description: 'The members of this group.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'SyncGuid', - displayName: 'Sync Guid', - description: 'GUID of corresponding AD object.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'LastSync', - displayName: 'Last synchronization', - description: 'Date of last synchronization.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Group', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-Group,DisplayName', + Description: '$Ctd-Group,Description', + Icon: 'Group', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['User', 'Group'], + SelectionRoots: ['/Root/IMS', '/Root'], + Name: 'Members', + DisplayName: 'Members', + Description: 'The members of this group.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'SyncGuid', + DisplayName: 'Sync Guid', + Description: 'GUID of corresponding AD object.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'LastSync', + DisplayName: 'Last synchronization', + Description: 'Date of last synchronization.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the ListItem - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.ListItem, - DisplayName: '$Ctd-ListItem,DisplayName', - Description: '$Ctd-ListItem,Description', - Icon: 'FormItem', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - ] - }), + { + ContentTypeName: 'ListItem', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-ListItem,DisplayName', + Description: '$Ctd-ListItem,Description', + Icon: 'FormItem', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + ] + }, - /** - * Method that returns the Content Type Definition of the CustomListItem - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.CustomListItem, - DisplayName: '$Ctd-CustomListItem,DisplayName', - Description: '$Ctd-CustomListItem,Description', - Icon: 'FormItem', - AllowIndexing: true, - AllowIncrementalNaming: true, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.NullFieldSetting({ - name: 'WorkflowsRunning', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'CustomListItem', + ParentTypeName: 'ListItem', + DisplayName: '$Ctd-CustomListItem,DisplayName', + Description: '$Ctd-CustomListItem,Description', + Icon: 'FormItem', + AllowIndexing: true, + AllowIncrementalNaming: true, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'NullFieldSetting', + Name: 'WorkflowsRunning', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Memo - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Memo, - DisplayName: '$Ctd-Memo,DisplayName', - Description: '$Ctd-Memo,Description', - Icon: 'Document', - AllowIndexing: true, - AllowIncrementalNaming: true, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'Date', - displayName: 'Date', - description: 'Please set the due date of the memo.', - readOnly: false, - compulsory: false, - defaultValue: '[Script:jScript] DateTime.UtcNow; [/Script]', - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: true, - allowMultiple: false, - options: [ - {Value: 'generic', Text: 'Generic', Enabled: true, Selected: true }, - {Value: 'iso', Text: 'ISO', Enabled: true, Selected: false }, - {Value: 'iaudit', Text: 'Internal audit', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'MemoType', - displayName: 'Memo type', - description: 'Type of the memo.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - name: 'SeeAlso', - displayName: 'See also...', - description: 'A list of content this memo pertains to.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Memo', + ParentTypeName: 'ListItem', + DisplayName: '$Ctd-Memo,DisplayName', + Description: '$Ctd-Memo,Description', + Icon: 'Document', + AllowIndexing: true, + AllowIncrementalNaming: true, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'Date', + DisplayName: 'Date', + Description: 'Please set the due date of the memo.', + ReadOnly: false, + Compulsory: false, + DefaultValue: '[Script:jScript] DateTime.UtcNow; [/Script]', + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: true, + AllowMultiple: false, + Options: [ + {Value: 'generic', Text: 'Generic', Enabled: true, Selected: true }, + {Value: 'iso', Text: 'ISO', Enabled: true, Selected: false }, + {Value: 'iaudit', Text: 'Internal audit', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'MemoType', + DisplayName: 'Memo type', + Description: 'Type of the memo.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + Name: 'SeeAlso', + DisplayName: 'See also...', + Description: 'A list of content this memo pertains to.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Task - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Task, - DisplayName: '$Ctd-Task,DisplayName', - Description: '$Ctd-Task,Description', - Icon: 'FormItem', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'StartDate', - displayName: 'Start date', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'DueDate', - displayName: 'Due date', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['User'], - selectionRoots: ['/Root/IMS', '/Root'], - name: 'AssignedTo', - displayName: 'Assigned to', - description: 'List of internal stakeholders.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '1', Text: 'Urgent', Enabled: true, Selected: false }, - {Value: '2', Text: 'Normal', Enabled: true, Selected: true }, - {Value: '3', Text: 'Not urgent', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'Priority', - displayName: 'Priority', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: 'pending', Text: 'Pending', Enabled: true, Selected: false }, - {Value: 'active', Text: 'Active', Enabled: true, Selected: true }, - {Value: 'completed', Text: 'Completed', Enabled: true, Selected: false }, - {Value: 'deferred', Text: 'Deferred', Enabled: true, Selected: false }, - {Value: 'waiting', Text: 'Waiting', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'Status', - displayName: 'Status', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - minValue: 0, - maxValue: 100, - showAsPercentage: true, - name: 'TaskCompletion', - displayName: 'Completion', - description: 'Completion percentage of the task.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.IntegerFieldSetting({ - name: 'RemainingDays', - displayName: 'Remaining days', - description: 'Number of remaining days.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'DueText', - displayName: 'DueText', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'DueCssClass', - displayName: 'Due style', - description: 'Css class', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Task', + ParentTypeName: 'ListItem', + DisplayName: '$Ctd-Task,DisplayName', + Description: '$Ctd-Task,Description', + Icon: 'FormItem', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'StartDate', + DisplayName: 'Start date', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'DueDate', + DisplayName: 'Due date', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['User'], + SelectionRoots: ['/Root/IMS', '/Root'], + Name: 'AssignedTo', + DisplayName: 'Assigned to', + Description: 'List of internal stakeholders.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '1', Text: 'Urgent', Enabled: true, Selected: false }, + {Value: '2', Text: 'Normal', Enabled: true, Selected: true }, + {Value: '3', Text: 'Not urgent', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'Priority', + DisplayName: 'Priority', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: 'pending', Text: 'Pending', Enabled: true, Selected: false }, + {Value: 'active', Text: 'Active', Enabled: true, Selected: true }, + {Value: 'completed', Text: 'Completed', Enabled: true, Selected: false }, + {Value: 'deferred', Text: 'Deferred', Enabled: true, Selected: false }, + {Value: 'waiting', Text: 'Waiting', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'Status', + DisplayName: 'Status', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'IntegerFieldSetting', + MinValue: 0, + MaxValue: 100, + ShowAsPercentage: true, + Name: 'TaskCompletion', + DisplayName: 'Completion', + Description: 'Completion percentage of the task.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'IntegerFieldSetting', + Name: 'RemainingDays', + DisplayName: 'Remaining days', + Description: 'Number of remaining days.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.IntegerFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'DueText', + DisplayName: 'DueText', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'DueCssClass', + DisplayName: 'Due style', + Description: 'Css class', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the Query - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.Query, - DisplayName: '$Ctd-Query,DisplayName', - Description: '$Ctd-Query,Description', - Icon: 'Query', - AllowIndexing: true, - AllowIncrementalNaming: true, - AllowedChildTypes: [], - FieldSettings: [ - new FieldSettings.LongTextFieldSetting({ - name: 'Query', - displayName: 'Query', - description: 'Query text.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:QueryBuilder' - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: 'Public', Text: 'Public', Enabled: true, Selected: true }, - {Value: 'Private', Text: 'Private', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.RadioButtons, - enumTypeName: '', - name: 'QueryType', - displayName: 'Query type', - description: 'Public queries are stored under the workspace, private queries are stored under the user profile.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - ] - }), + { + ContentTypeName: 'Query', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-Query,DisplayName', + Description: '$Ctd-Query,Description', + Icon: 'Query', + AllowIndexing: true, + AllowIncrementalNaming: true, + AllowedChildTypes: [], + FieldSettings: [ + { + Type: 'LongTextFieldSetting', + Name: 'Query', + DisplayName: 'Query', + Description: 'Query text.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:QueryBuilder' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: 'Public', Text: 'Public', Enabled: true, Selected: true }, + {Value: 'Private', Text: 'Private', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.RadioButtons, + EnumTypeName: '', + Name: 'QueryType', + DisplayName: 'Query type', + Description: 'Public queries are stored under the workspace, private queries are stored under the user profile.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + ] + }, - /** - * Method that returns the Content Type Definition of the User - * @returns {Schema} - */ - new Schema({ - ContentType: ContentTypes.User, - DisplayName: '$Ctd-User,DisplayName', - Description: '$Ctd-User,Description', - Icon: 'User', - AllowIndexing: true, - AllowIncrementalNaming: false, - AllowedChildTypes: ['Image'], - FieldSettings: [ - new FieldSettings.ShortTextFieldSetting({ - maxLength: 100, - name: 'LoginName', - displayName: 'Login name', - description: 'The name that the user has to type in on login forms (in some cases along with the domain name). It has to be unique under a domain.', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'JobTitle', - displayName: 'Job title', - description: '', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Enabled', - displayName: 'Enabled', - description: 'User account is enabled.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Domain', - displayName: 'Domain', - description: 'The domain of the user.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - regex: '^[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}$', - name: 'Email', - displayName: 'E-mail', - description: 'The e-mail of the user.', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - regex: '[^<]+', - name: 'FullName', - displayName: 'Full name', - description: 'Full name of the user (e.g. John Smith).', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - name: 'ImageRef', - displayName: 'Cover image (reference)', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.BinaryFieldSetting({ - name: 'ImageData', - displayName: 'Cover image (binarydata)', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.NullFieldSetting({ - name: 'Avatar', - displayName: 'Avatar', - description: 'Avatar image of user.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0, - controlHint: 'sn:Image' - }), - new FieldSettings.PasswordFieldSetting({ - reenterTitle: 'Re-enter password', - reenterDescription: 'Re-enter password.', - passwordHistoryLength: 0, - name: 'Password', - displayName: 'Password', - description: 'User password.', - readOnly: false, - compulsory: true, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'SyncGuid', - displayName: 'SyncGuid', - description: 'GUID of corresponding AD object.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.DateAndTime, - name: 'LastSync', - displayName: 'LastSync', - description: 'Date of last synchronization.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.CaptchaFieldSetting({ - name: 'Captcha', - displayName: 'Captcha text', - description: 'Captcha text entered by the user.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: false, - allowedTypes: ['User'], - selectionRoots: ['/Root/IMS'], - name: 'Manager', - displayName: 'Manager', - description: 'Manager of the user.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Department', - displayName: 'Department', - description: 'Department of employee.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'Languages', - displayName: 'Languages', - description: 'Spoken languages.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - regex: '(^\\d*([-\\s\\+\\(\\)]\\d*)*$)?', - name: 'Phone', - displayName: 'Phone', - description: 'Phone number. (e.g. +123456789 or 1234).', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '...', Text: '...', Enabled: true, Selected: false }, - {Value: 'Female', Text: 'Female', Enabled: true, Selected: false }, - {Value: 'Male', Text: 'Male', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'Gender', - displayName: 'Gender', - description: 'Select one.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: '...', Text: '...', Enabled: true, Selected: false }, - {Value: 'Single', Text: 'Single', Enabled: true, Selected: false }, - {Value: 'Married', Text: 'Married', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'MaritalStatus', - displayName: 'Marital status', - description: 'Select one.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.DateTimeFieldSetting({ - dateTimeMode: FieldSettings.DateTimeMode.Date, - name: 'BirthDate', - displayName: 'Birth date', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.LongTextFieldSetting({ - textType: FieldSettings.TextType.LongText, - name: 'Education', - displayName: 'Education', - description: 'List of educations - e.g. high school, university.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0, - controlHint: 'sn:EducationEditor' - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'TwitterAccount', - displayName: 'Twitter account', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'FacebookURL', - displayName: 'Facebook URL', - description: 'http://www.facebook.com/USERNAME.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'LinkedInURL', - displayName: 'LinkedIn URL', - description: 'http://www.linkedin.com/USERNAME.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ChoiceFieldSetting({ - allowExtraValue: false, - allowMultiple: false, - options: [ - {Value: 'en', Text: 'English', Enabled: true, Selected: true }, - {Value: 'hu', Text: 'Hungarian', Enabled: true, Selected: false } - ], - displayChoice: FieldSettings.DisplayChoice.DropDown, - enumTypeName: '', - name: 'Language', - displayName: 'Selected language', - description: 'Language used to display texts on the site, if it is available.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Show, - visibleEdit: FieldSettings.FieldVisibility.Show, - visibleNew: FieldSettings.FieldVisibility.Show, - defaultOrder: 0 - }), - new FieldSettings.ReferenceFieldSetting({ - allowMultiple: true, - allowedTypes: ['Workspace'], - name: 'FollowedWorkspaces', - displayName: 'Followed workspaces', - description: 'List of workspaces followed by the user.', - readOnly: false, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Advanced, - visibleEdit: FieldSettings.FieldVisibility.Advanced, - visibleNew: FieldSettings.FieldVisibility.Advanced, - defaultOrder: 0 - }), - new FieldSettings.ShortTextFieldSetting({ - name: 'ProfilePath', - displayName: 'Profile path', - description: 'Path of the user\'s personal workspace.', - readOnly: true, - compulsory: false, - visibleBrowse: FieldSettings.FieldVisibility.Hide, - visibleEdit: FieldSettings.FieldVisibility.Hide, - visibleNew: FieldSettings.FieldVisibility.Hide, - defaultOrder: 0 - }), - ] - }), - -] + { + ContentTypeName: 'User', + ParentTypeName: 'GenericContent', + DisplayName: '$Ctd-User,DisplayName', + Description: '$Ctd-User,Description', + Icon: 'User', + AllowIndexing: true, + AllowIncrementalNaming: false, + AllowedChildTypes: ['Image'], + FieldSettings: [ + { + Type: 'ShortTextFieldSetting', + MaxLength: 100, + Name: 'LoginName', + DisplayName: 'Login name', + Description: 'The name that the user has to type in on login forms (in some cases along with the domain name). It has to be unique under a domain.', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'JobTitle', + DisplayName: 'Job title', + Description: '', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Enabled', + DisplayName: 'Enabled', + Description: 'User account is enabled.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.NullFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Domain', + DisplayName: 'Domain', + Description: 'The domain of the user.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Regex: '^[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}$', + Name: 'Email', + DisplayName: 'E-mail', + Description: 'The e-mail of the user.', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Regex: '[^<]+', + Name: 'FullName', + DisplayName: 'Full name', + Description: 'Full name of the user (e.g. John Smith).', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + Name: 'ImageRef', + DisplayName: 'Cover image (reference)', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'BinaryFieldSetting', + Name: 'ImageData', + DisplayName: 'Cover image (binarydata)', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.BinaryFieldSetting, + { + Type: 'NullFieldSetting', + Name: 'Avatar', + DisplayName: 'Avatar', + Description: 'Avatar image of user.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0, + ControlHint: 'sn:Image' + } as FieldSettings.NullFieldSetting, + { + Type: 'PasswordFieldSetting', + ReenterTitle: 'Re-enter password', + ReenterDescription: 'Re-enter password.', + PasswordHistoryLength: 0, + Name: 'Password', + DisplayName: 'Password', + Description: 'User password.', + ReadOnly: false, + Compulsory: true, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.PasswordFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'SyncGuid', + DisplayName: 'SyncGuid', + Description: 'GUID of corresponding AD object.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.DateAndTime, + Name: 'LastSync', + DisplayName: 'LastSync', + Description: 'Date of last synchronization.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'CaptchaFieldSetting', + Name: 'Captcha', + DisplayName: 'Captcha text', + Description: 'Captcha text entered by the user.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.CaptchaFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: false, + AllowedTypes: ['User'], + SelectionRoots: ['/Root/IMS'], + Name: 'Manager', + DisplayName: 'Manager', + Description: 'Manager of the user.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Department', + DisplayName: 'Department', + Description: 'Department of employee.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'Languages', + DisplayName: 'Languages', + Description: 'Spoken languages.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Regex: '(^\\d*([-\\s\\+\\(\\)]\\d*)*$)?', + Name: 'Phone', + DisplayName: 'Phone', + Description: 'Phone number. (e.g. +123456789 or 1234).', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '...', Text: '...', Enabled: true, Selected: false }, + {Value: 'Female', Text: 'Female', Enabled: true, Selected: false }, + {Value: 'Male', Text: 'Male', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'Gender', + DisplayName: 'Gender', + Description: 'Select one.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: '...', Text: '...', Enabled: true, Selected: false }, + {Value: 'Single', Text: 'Single', Enabled: true, Selected: false }, + {Value: 'Married', Text: 'Married', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'MaritalStatus', + DisplayName: 'Marital status', + Description: 'Select one.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'DateTimeFieldSetting', + DateTimeMode: FieldSettings.DateTimeMode.Date, + Name: 'BirthDate', + DisplayName: 'Birth date', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.DateTimeFieldSetting, + { + Type: 'LongTextFieldSetting', + TextType: FieldSettings.TextType.LongText, + Name: 'Education', + DisplayName: 'Education', + Description: 'List of educations - e.g. high school, university.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0, + ControlHint: 'sn:EducationEditor' + } as FieldSettings.LongTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'TwitterAccount', + DisplayName: 'Twitter account', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'FacebookURL', + DisplayName: 'Facebook URL', + Description: 'http://www.facebook.com/USERNAME.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'LinkedInURL', + DisplayName: 'LinkedIn URL', + Description: 'http://www.linkedin.com/USERNAME.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + { + Type: 'ChoiceFieldSetting', + AllowExtraValue: false, + AllowMultiple: false, + Options: [ + {Value: 'en', Text: 'English', Enabled: true, Selected: true }, + {Value: 'hu', Text: 'Hungarian', Enabled: true, Selected: false } + ], + DisplayChoice: FieldSettings.DisplayChoice.DropDown, + EnumTypeName: '', + Name: 'Language', + DisplayName: 'Selected language', + Description: 'Language used to display texts on the site, if it is available.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Show, + VisibleEdit: FieldSettings.FieldVisibility.Show, + VisibleNew: FieldSettings.FieldVisibility.Show, + DefaultOrder: 0 + } as FieldSettings.ChoiceFieldSetting, + { + Type: 'ReferenceFieldSetting', + AllowMultiple: true, + AllowedTypes: ['Workspace'], + Name: 'FollowedWorkspaces', + DisplayName: 'Followed workspaces', + Description: 'List of workspaces followed by the user.', + ReadOnly: false, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Advanced, + VisibleEdit: FieldSettings.FieldVisibility.Advanced, + VisibleNew: FieldSettings.FieldVisibility.Advanced, + DefaultOrder: 0 + } as FieldSettings.ReferenceFieldSetting, + { + Type: 'ShortTextFieldSetting', + Name: 'ProfilePath', + DisplayName: 'Profile path', + Description: 'Path of the user\'s personal workspace.', + ReadOnly: true, + Compulsory: false, + VisibleBrowse: FieldSettings.FieldVisibility.Hide, + VisibleEdit: FieldSettings.FieldVisibility.Hide, + VisibleNew: FieldSettings.FieldVisibility.Hide, + DefaultOrder: 0 + } as FieldSettings.ShortTextFieldSetting, + ] + }, +]; diff --git a/src/Security.ts b/src/Security.ts index b8ffcef..a98ec0d 100644 --- a/src/Security.ts +++ b/src/Security.ts @@ -4,59 +4,61 @@ * @description Module for security related stuff */ /** */ + // tslint:disable:naming-convention + /** * Provides metadata about identity kind. -*/ + */ export enum IdentityKind { All, Users, Groups, OrganizationalUnits, UsersAndGroups, UsersAndOrganizationalUnits, GroupsAndOrganizationalUnits } /** * Provides metadata about permission level. -*/ + */ export enum PermissionLevel { AllowedOrDenied, Allowed, Denied } /** -* Type to provide an Object with the permission information that has to be set. -*/ + * Type to provide an Object with the permission information that has to be set. + */ export class PermissionRequestBody { - identity: string; - localOnly?: boolean; - RestrictedPreview?: PermissionValues; - PreviewWithoutWatermakr?: PermissionValues; - PreviewWithoutRedaction?: PermissionValues; - Open?: PermissionValues; - OpenMinor?: PermissionValues; - Save?: PermissionValues; - Publish?: PermissionValues; - ForceUndoCheckout?: PermissionValues; - AddNew?: PermissionValues; - Approve?: PermissionValues; - Delete?: PermissionValues; - RecallOldVersion?: PermissionValues; - DeleteOldVersion?: PermissionValues; - SeePermissions?: PermissionValues; - SetPermissions?: PermissionValues; - RunApplication?: PermissionValues; - ManageListsAndWorkspaces?: PermissionValues; - TakeOwnership?: PermissionValues; - Custom01?: PermissionValues; - Custom02?: PermissionValues; - Custom03?: PermissionValues; - Custom04?: PermissionValues; - Custom05?: PermissionValues; - Custom06?: PermissionValues; - Custom07?: PermissionValues; - Custom08?: PermissionValues; - Custom09?: PermissionValues; - Custom10?: PermissionValues; - Custom11?: PermissionValues; - Custom12?: PermissionValues; - Custom13?: PermissionValues; - Custom14?: PermissionValues; + public identity: string; + public localOnly?: boolean; + public RestrictedPreview?: PermissionValues; + public PreviewWithoutWatermakr?: PermissionValues; + public PreviewWithoutRedaction?: PermissionValues; + public Open?: PermissionValues; + public OpenMinor?: PermissionValues; + public Save?: PermissionValues; + public Publish?: PermissionValues; + public ForceUndoCheckout?: PermissionValues; + public AddNew?: PermissionValues; + public Approve?: PermissionValues; + public Delete?: PermissionValues; + public RecallOldVersion?: PermissionValues; + public DeleteOldVersion?: PermissionValues; + public SeePermissions?: PermissionValues; + public SetPermissions?: PermissionValues; + public RunApplication?: PermissionValues; + public ManageListsAndWorkspaces?: PermissionValues; + public TakeOwnership?: PermissionValues; + public Custom01?: PermissionValues; + public Custom02?: PermissionValues; + public Custom03?: PermissionValues; + public Custom04?: PermissionValues; + public Custom05?: PermissionValues; + public Custom06?: PermissionValues; + public Custom07?: PermissionValues; + public Custom08?: PermissionValues; + public Custom09?: PermissionValues; + public Custom10?: PermissionValues; + public Custom11?: PermissionValues; + public Custom12?: PermissionValues; + public Custom13?: PermissionValues; + public Custom14?: PermissionValues; } /** * Provides metadata about permission values. -*/ + */ export enum PermissionValues { undefined = 0, allow = 1, deny = 2 } /** * Provides metadata about permission inheritance. */ -export enum Inheritance { 'break', 'unbreak' }; +export enum Inheritance { 'break', 'unbreak' } diff --git a/test/BinaryFieldTests.ts b/test/BinaryFieldTests.ts index 8d9f42c..81a3833 100644 --- a/test/BinaryFieldTests.ts +++ b/test/BinaryFieldTests.ts @@ -1,40 +1,49 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { MediaResourceObject } from '../src/ComplexTypes'; -import { File as SnFile } from '../src/ContentTypes'; import { BinaryField } from '../src/BinaryField'; import { SavedContent } from '../src/Content'; -import { BinaryFieldSetting } from '../src/FieldSettings'; +import { File as SnFile } from '../src/ContentTypes'; import { UploadFileOptions, WithParentContent } from '../src/Repository/UploadModels'; +import { MockRepository } from './Mocks'; const expect = Chai.expect; @suite('BinaryField') export class BinaryFieldTests { - private _fieldSetting: BinaryFieldSetting; private _file: SavedContent; - before() { - this._fieldSetting = new BinaryFieldSetting({ - name: 'Binary' - }); - this._file = { + + private _repo: MockRepository; + // tslint:disable-next-line:naming-convention + public before() { + + this._repo = new MockRepository(); + + this._file = this._repo.HandleLoadedContent({ + Id: 123, + Path: 'Root/Examples/ExampleFile', + Name: 'test', + Type: 'File', Binary: { __mediaresource: { media_src: 'https://google.com' } } - } as SavedContent + } as any, SnFile); + } + + private createField(): BinaryField { + return this._file.Binary as BinaryField; } @test public 'Can be constructed'() { - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); expect(field).to.be.instanceof(BinaryField); } @test public 'MediaResourceObject should be available'() { - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); expect(field.GetMediaResourceObject()).to.be.instanceof(Object); expect(field.GetMediaResourceObject().__mediaresource).to.be.instanceof(Object); expect(field.GetMediaResourceObject().__mediaresource.media_src).to.be.eq('https://google.com'); @@ -42,55 +51,61 @@ export class BinaryFieldTests { @test public 'DownloadUrl should be available'() { - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); expect(field.GetDownloadUrl()).to.be.eq('https://google.com'); } @test - public 'Parent.GetFullPath() should return the Content path'(done: MochaDone) { - (this._file as any)['GetRepository'] = () => { + public 'DownloadUrl should be available without MediaResourceObject'() { + const field = new BinaryField(null as any, this._file, {Name: 'Binary'} as any); + expect(field.GetDownloadUrl()).to.be.eq('/binaryhandler.ashx?nodeid=123&propertyname=Binary'); + } + + @test + public 'Parent.GetFullPath() should return the ParentContentPath'(done: MochaDone) { + (this._file as any).GetRepository = () => { return { UploadFile: (options: WithParentContent>) => { - expect(options.Parent.GetFullPath()).to.be.eq(this._file.Path); + expect(options.Parent.GetFullPath()).to.be.eq(this._file.ParentContentPath); expect(options).to.be.instanceof(Object); done(); } - } + }; }; - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); field.SaveBinaryFile(new File(['alma'], 'alma.txt')); } @test public 'SaveBinaryFile() should trigger an upload request'(done: MochaDone) { - (this._file as any)['GetRepository'] = () => { + (this._file as any).GetRepository = () => { return { UploadFile: (options: UploadFileOptions) => { - + expect(options.Overwrite).to.be.eq(true); + expect(options.File.name).to.be.eq(this._file.Name); expect(options).to.be.instanceof(Object); - done(); } - } + }; }; - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); field.SaveBinaryFile(new File(['alma'], 'alma.txt')); } @test public 'SaveBinaryText() should trigger an upload request'(done: MochaDone) { - (this._file as any)['GetRepository'] = () => { + (this._file as any).GetRepository = () => { return { UploadFile: (options: UploadFileOptions) => { expect(options).to.be.instanceof(Object); done(); } - } + }; }; - const field = new BinaryField(this._file.Binary as MediaResourceObject, this._file, this._fieldSetting); + const field = this.createField(); field.SaveBinaryText('alma'); } diff --git a/test/CollectionTests.ts b/test/CollectionTests.ts index c379cc8..8ee212f 100644 --- a/test/CollectionTests.ts +++ b/test/CollectionTests.ts @@ -1,34 +1,34 @@ -import { Observable } from '@reactivex/rxjs'; import * as Chai from 'chai'; -import { Collection } from '../src/Collection'; -import { Content } from '../src/Content'; -import { MockRepository } from './Mocks/MockRepository'; +import { Observable } from 'rxjs/Observable'; import { LoginState } from '../src/Authentication/LoginState'; +import { Collection } from '../src/Collection'; +import { Content, ContentInternal } from '../src/Content'; +import { File as SnFile } from '../src/ContentTypes'; import { Task } from '../src/ContentTypes'; +import { MockRepository } from './Mocks/MockRepository'; const expect = Chai.expect; export const CollectionTests = describe('Collection', () => { - let collection: Collection; - let children: Content[]; + let collection: Collection; + let children: Content[]; let Repo: MockRepository; beforeEach(() => { - Repo = new MockRepository() + Repo = new MockRepository(); children = [ Repo.HandleLoadedContent({ Id: 1, Name: 'test1', Path: '/' - }, Content), + }), Repo.HandleLoadedContent({ Id: 2, Path: 'Root/Test', Name: 'test2' - }, Content)]; - + })]; - collection = new Collection(children, Repo); + collection = new Collection(children, Repo, Task); collection.Path = 'https://daily.demo.sensenet.com/lorem'; }); describe('#Items()', () => { @@ -44,7 +44,7 @@ export const CollectionTests = describe('Collection', () => { describe('#Item(id)', () => { it('should return an object with a given id', () => { const item = collection.Item(1); - expect(item).to.be.instanceof(Content); + expect(item).to.be.instanceof(ContentInternal); if (item && item.Id) { expect(item.Id).to.be.eq(1); } else { @@ -61,17 +61,17 @@ export const CollectionTests = describe('Collection', () => { }); describe('#Add()', () => { it('should return an observable', () => { - let content = Content.Create({ DueDate: '2017-06-27T11:11:11Z', Name: '' }, Task, Repo); - expect(collection.Add(content.options)).to.be.instanceof(Observable); + const content = ContentInternal.Create({ DueDate: '2017-06-27T11:11:11Z', Name: '' }, Task, Repo); + expect(collection.Add(content)).to.be.instanceof(Observable); }); it('Observable should be resolved', (done) => { - let content = Repo.HandleLoadedContent({ DueDate: '2017-06-27T11:11:11Z', Name: '', Id: 231876, Path: 'Root/Test' }, Task); + const content = Repo.HandleLoadedContent({ DueDate: '2017-06-27T11:11:11Z', Name: '', Id: 231876, Path: 'Root/Test' }, Task); Repo.Authentication.StateSubject.next(LoginState.Authenticated); Repo.HttpProviderRef.AddResponse({ d: content.GetFields() }); - collection.Add(content.options).subscribe(r => { - done() - }, done) + collection.Add(content).subscribe((r) => { + done(); + }, done); }); }); @@ -79,21 +79,22 @@ export const CollectionTests = describe('Collection', () => { it('should return an observable', () => { expect(collection.Remove(1, true)).to.be.instanceof(Observable); }); - }); - describe('#Remove()', () => { + it('should return an observable', () => { expect(collection.Remove(1)).to.be.instanceof(Observable); }); - }); - describe('#Remove()', () => { + + it('should return an observable', () => { + expect(collection.Remove(999)).to.be.instanceof(Observable); + }); + it('should return an observable', () => { - collection['Path'] = '/workspaces/project' + collection.Path = '/workspaces/project'; expect(collection.Remove([0, 1], true)).to.be.instanceof(Observable); }); - }); - describe('#Remove()', () => { + it('should return an observable', () => { - collection['Path'] = '/workspaces/project' + collection.Path = '/workspaces/project'; expect(collection.Remove([0, 1])).to.be.instanceof(Observable); }); }); @@ -104,7 +105,7 @@ export const CollectionTests = describe('Collection', () => { }); describe('#Move()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project' + collection.Path = '/workspaces/project'; expect(collection.Move([0, 1], '/workspaces/Project')).to.be.instanceof(Observable); }); }); @@ -115,36 +116,36 @@ export const CollectionTests = describe('Collection', () => { }); describe('#Copy()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project' + collection.Path = '/workspaces/project'; expect(collection.Copy([0, 1], '/workspaces/Project')).to.be.instanceof(Observable); }); }); describe('#AllowedChildTypes()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project'; + collection.Path = '/workspaces/project'; expect(collection.AllowedChildTypes()).to.be.instanceof(Observable); }); }); describe('#AllowedChildTypes()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project'; + collection.Path = '/workspaces/project'; expect(collection.AllowedChildTypes({ select: 'Name' })).to.be.instanceof(Observable); }); }); describe('#Upload()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project'; - expect(collection.Upload('Task', 'task.docx')).to.be.instanceof(Observable); + collection.Path = '/workspaces/project'; + expect(collection.Upload({Text: 'alma', FileName: 'alma.txt', Parent: collection.Items[0], PropertyName: 'Binary', ContentType: SnFile, Body: {}, Overwrite: true})).to.be.instanceof(Observable); }); }); describe('#Read()', () => { it('should return an observable', () => { - collection['Path'] = '/workspaces/project'; + collection.Path = '/workspaces/project'; expect(collection.Read('Task')).to.be.instanceof(Observable); }); it('should update from Result', (done) => { - collection['Path'] = '/workspaces/project'; + collection.Path = '/workspaces/project'; Repo.Authentication.StateSubject.next(LoginState.Authenticated); Repo.HttpProviderRef.AddResponse({ d: { @@ -156,11 +157,11 @@ export const CollectionTests = describe('Collection', () => { Type: 'User', }] } - }) - collection.Read('Task').subscribe(result => { + }); + collection.Read('Task').subscribe((result) => { expect(result.length).to.be.eq(1); done(); }, done); }); }); -}); \ No newline at end of file +}); diff --git a/test/ComplexTypesTests.ts b/test/ComplexTypesTests.ts index 541e91f..f1b3ca2 100644 --- a/test/ComplexTypesTests.ts +++ b/test/ComplexTypesTests.ts @@ -1,5 +1,5 @@ -import { ChoiceOption, DeferredObject, DeferredUriObject, MediaResourceObject, MediaObject } from '../src/ComplexTypes'; import * as Chai from 'chai'; +import { ChoiceOption, DeferredObject, DeferredUriObject, MediaObject, MediaResourceObject } from '../src/ComplexTypes'; const expect = Chai.expect; export const ComplexTypesTests = describe('ComplexTypes', () => { @@ -33,4 +33,4 @@ export const ComplexTypesTests = describe('ComplexTypes', () => { expect(link).to.be.an.instanceof(MediaObject); }); }); -}); \ No newline at end of file +}); diff --git a/test/ContentListReferenceFieldTests.ts b/test/ContentListReferenceFieldTests.ts index aa55917..e7bca7a 100644 --- a/test/ContentListReferenceFieldTests.ts +++ b/test/ContentListReferenceFieldTests.ts @@ -1,22 +1,25 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { ContentListReferenceField } from '../src/ContentReferences'; -import { DeferredObject } from '../src/ComplexTypes'; -import { MockRepository } from './Mocks/MockRepository'; -import { IContentOptions } from '../src/Content'; import { LoginState } from '../src/Authentication/LoginState'; -import { ReferenceFieldSetting } from '../src/FieldSettings'; +import { DeferredObject } from '../src/ComplexTypes'; +import {ContentInternal} from '../src/Content'; +import { ContentListReferenceField } from '../src/ContentReferences'; import { Task } from '../src/ContentTypes'; +import { ReferenceFieldSetting } from '../src/FieldSettings'; +import { MockRepository } from './Mocks/MockRepository'; const expect = Chai.expect; +// tslint:disable:no-string-literal + @suite('ContentListReferenceField') export class ContentListReferenceFieldTests { private _unloadedRef: ContentListReferenceField; private _loadedRef: ContentListReferenceField; private _repo: MockRepository; - before() { + // tslint:disable-next-line:naming-convention + public before() { this._repo = new MockRepository(); this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._loadedRef = new ContentListReferenceField( @@ -24,13 +27,14 @@ export class ContentListReferenceFieldTests { Id: 1, Path: 'root/a/b', Name: 'Name', - Type: 'Task' - } as IContentOptions], new ReferenceFieldSetting({}), this._repo); + Type: 'Task', + DueText: 'testDueText' + } as Task], {} as ReferenceFieldSetting, this._repo); this._unloadedRef = new ContentListReferenceField({ __deferred: { uri: 'a/b/c' } - } as DeferredObject, new ReferenceFieldSetting({}), this._repo); + } as DeferredObject, {} as ReferenceFieldSetting, this._repo); } @test @@ -42,10 +46,10 @@ export class ContentListReferenceFieldTests { @test public 'Should be able to construct ContentReferenceField from IContentOptions with loaded content reference'() { expect(this._loadedRef).to.be.instanceof(ContentListReferenceField); - expect(this._loadedRef['_contentReferences'][0]).to.be.instanceOf(Task) + expect(this._loadedRef['_contentReferences'][0]).to.be.instanceOf(ContentInternal); + expect(this._loadedRef['_contentReferences'][0].DueText).to.be.eq('testDueText'); } - @test public 'Getting unloaded referenced Content should trigger an OData call'(done: MochaDone) { this._repo.HttpProviderRef.AddResponse({ @@ -58,37 +62,37 @@ export class ContentListReferenceFieldTests { Type: 'Workspace' }] } - }) - this._unloadedRef.GetContent().subscribe(c => { + }); + this._unloadedRef.GetContent().subscribe((c) => { expect(this._unloadedRef['_contentReferences']).to.be.eq(c); done(); - }, err => done); + }, (err) => done); } @test public 'Getting loaded referenced Content should NOT trigger an OData call'(done: MochaDone) { - this._loadedRef.GetContent().subscribe(c => { + this._loadedRef.GetContent().subscribe((c) => { expect(this._loadedRef['_contentReferences']).to.be.eq(c); done(); - }, err => done); + }, (err) => done); } @test public 'getValue should return undefined for unloaded reference'() { - expect(this._unloadedRef.getValue()).to.be.eq(undefined); + expect(this._unloadedRef.GetValue()).to.be.eq(undefined); } @test public 'getValue should return the loaded Path for a loaded reference'() { - expect(this._loadedRef.getValue()).to.be.deep.eq(this._loadedRef['_contentReferences'].map(p => p.Path)); + expect(this._loadedRef.GetValue()).to.be.deep.eq(this._loadedRef['_contentReferences'].map((p) => p.Path)); } @test public 'SetContent should set the reference content'(done: MochaDone) { this._unloadedRef.SetContent(this._loadedRef['_contentReferences']); - this._unloadedRef.GetContent().subscribe(c => { + this._unloadedRef.GetContent().subscribe((c) => { expect(c).to.eq(this._loadedRef['_contentReferences']); done(); - }, err => done) + }, (err) => done); } } diff --git a/test/ContentReferenceFieldTests.ts b/test/ContentReferenceFieldTests.ts index 17b52a2..4ab33f8 100644 --- a/test/ContentReferenceFieldTests.ts +++ b/test/ContentReferenceFieldTests.ts @@ -1,14 +1,16 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { ContentReferenceField } from '../src/ContentReferences'; -import { DeferredObject } from '../src/ComplexTypes'; -import { MockRepository } from './Mocks/MockRepository'; import { LoginState } from '../src/Authentication/LoginState'; +import { DeferredObject } from '../src/ComplexTypes'; +import { ContentInternal } from '../src/Content'; +import { ContentReferenceField } from '../src/ContentReferences'; +import { Task } from '../src/ContentTypes'; import { ReferenceFieldSetting } from '../src/FieldSettings'; import { FinializedQuery } from '../src/Query/index'; -import { Task } from '../src/ContentTypes'; +import { MockRepository } from './Mocks/MockRepository'; const expect = Chai.expect; +// tslint:disable:no-string-literal @suite('ContentReferenceField') export class ContentReferenceFieldTests { @@ -16,35 +18,39 @@ export class ContentReferenceFieldTests { private _loadedRef: ContentReferenceField; private _repo: MockRepository; - before() { + // tslint:disable-next-line:naming-convention + public before() { this._repo = new MockRepository(); this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - this._loadedRef = new ContentReferenceField({ + + this._loadedRef = new ContentReferenceField(this._repo.HandleLoadedContent({ Id: 1, Path: 'root/a/b', Name: 'Name', - Type: 'Task' - }, new ReferenceFieldSetting({}), this._repo); + Type: 'Task', + DueText: 'testDueText' + }), {} as ReferenceFieldSetting, this._repo); this._unloadedRef = new ContentReferenceField({ __deferred: { uri: 'a/b/c' } - } as DeferredObject, new ReferenceFieldSetting({}), this._repo); + } as DeferredObject, {} as ReferenceFieldSetting, this._repo); } @test public 'Should be able to construct ContentReferenceField from Deferred without loaded content reference'() { expect(this._unloadedRef).to.be.instanceof(ContentReferenceField); + expect(this._unloadedRef['_contentReference']).to.be.eq(undefined); } @test public 'Should be able to construct ContentReferenceField from IContentOptions with loaded content reference'() { expect(this._loadedRef).to.be.instanceof(ContentReferenceField); - expect(this._loadedRef['_contentReference']).to.be.instanceOf(Task) + expect(this._loadedRef['_contentReference']).to.be.instanceOf(ContentInternal); + expect(this._loadedRef['_contentReference'].DueText).to.be.eq('testDueText'); } - @test public 'Getting unloaded referenced Content should trigger an OData call'(done: MochaDone) { this._repo.HttpProviderRef.AddResponse({ @@ -55,64 +61,70 @@ export class ContentReferenceFieldTests { Path: 'Root/Workspace', Type: 'Workspace' } - }) - this._unloadedRef.GetContent().subscribe(c => { + }); + this._unloadedRef.GetContent().subscribe((c) => { expect(this._unloadedRef['_contentReference']).to.be.eq(c); done(); - }, err => done); + }, (err) => done); } @test public 'Getting loaded referenced Content should NOT trigger an OData call'(done: MochaDone) { - this._loadedRef.GetContent().subscribe(c => { + this._loadedRef.GetContent().subscribe((c) => { expect(this._loadedRef['_contentReference']).to.be.eq(c); done(); - }, err => done); + }, (err) => done); } @test - public 'getValue should return undefined for unloaded reference'(){ - expect(this._unloadedRef.getValue()).to.be.eq(undefined); + public 'getValue should return undefined for unloaded reference'() { + expect(this._unloadedRef.GetValue()).to.be.eq(undefined); } @test - public 'getValue should return the loaded Path for a loaded reference'(){ - expect(this._loadedRef.getValue()).to.be.eq(this._loadedRef['_contentReference'].Path); + public 'getValue should return the loaded Path for a loaded reference'() { + expect(this._loadedRef.GetValue()).to.be.eq(this._loadedRef['_contentReference'].Path); } @test - public 'SetContent should set the reference content'(done: MochaDone){ + public 'SetContent should set the reference content'(done: MochaDone) { this._unloadedRef.SetContent(this._loadedRef['_contentReference']); - this._unloadedRef.GetContent().subscribe(c => { + this._unloadedRef.GetContent().subscribe((c) => { expect(c).to.eq(this._loadedRef['_contentReference']); done(); - }, err => done) + }, (err) => done); } @test - public 'Search should return a FinializedQuery instance'(){ + public 'Search should return a FinializedQuery instance'() { const search = this._unloadedRef.Search(''); expect(search).to.be.instanceof(FinializedQuery); } @test - public 'Search query should contain the term and default parameters'(){ + public 'Search query should contain the term and default parameters'() { const search = this._unloadedRef.Search('test-term'); expect(search.toString()).to.be.eq('_Text:\'*test-term*\' .TOP:10 .SKIP:0'); } - @test - public 'Search query should contain selection roots if available'(){ + public 'Search query should contain selection roots if available'() { this._unloadedRef.FieldSetting.SelectionRoots = ['Root/Example1', 'Root/Example2']; const search = this._unloadedRef.Search('test-term'); expect(search.toString()).to.be.eq('_Text:\'*test-term*\' AND (InTree:"Root/Example1" OR InTree:"Root/Example2") .TOP:10 .SKIP:0'); } @test - public 'Search query should contain allowed types if available'(){ + public 'Search query should contain allowed types if available'() { this._unloadedRef.FieldSetting.AllowedTypes = ['Task', 'Folder']; const search = this._unloadedRef.Search('test-term'); expect(search.toString()).to.be.eq('_Text:\'*test-term*\' AND (Type:Task OR Type:Folder) .TOP:10 .SKIP:0'); } + + @test + public 'Search query should not add type filter if not defined'() { + this._unloadedRef.FieldSetting.AllowedTypes = []; + const search = this._unloadedRef.Search('test-term'); + expect(search.toString()).to.be.eq('_Text:\'*test-term*\' .TOP:10 .SKIP:0'); + } } diff --git a/test/ContentSerializerTests.ts b/test/ContentSerializerTests.ts index a2028c5..77ec96d 100644 --- a/test/ContentSerializerTests.ts +++ b/test/ContentSerializerTests.ts @@ -1,55 +1,57 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import { SavedContent } from '../src/Content'; +import { Task } from '../src/ContentTypes'; import { MockRepository } from './Mocks/MockRepository'; -import { ContentTypes } from '../src/SN'; - const expect = Chai.expect; @suite('ContentSerializer') export class ContentSerializerTests { - private _content: ContentTypes.Task; + private _content: SavedContent; - private _contentSerializedString = '{"Data":{"Id":3,"Path":"root/task1"},"Origin":"https://mock_repo_one/odata.svc/root/task1"}'; + private _contentSerializedString = '{"Data":{"Id":3,"Name":"test","Path":"root/task1"},"Origin":"https://mock_repo_one/odata.svc/root/task1"}'; private _repo: MockRepository; private _repo2: MockRepository; - before(){ + // tslint:disable-next-line:naming-convention + public before() { this._repo = new MockRepository({ RepositoryUrl: 'https://mock_repo_one' }); this._repo2 = new MockRepository({ RepositoryUrl: 'https://mock_repo_two' - }) + }); this._content = this._repo.HandleLoadedContent({ Id: 3, Path: 'root/task1', - Type: 'Task' - }); + Type: 'Task', + Name: 'test' + }, Task); } @test - public 'content.Stringify() should create a valid output'(){ + public 'content.Stringify() should create a valid output'() { const serialized = this._content.Stringify(); expect(serialized).to.be.eq(this._contentSerializedString); } @test - public 'content.Stringify() should throw an error when no Path specified'(){ + public 'content.Stringify() should throw an error when no Path specified'() { this._content.Path = ''; expect(() => this._content.Stringify()).to.throw(); } @test - public 'Repository.Parse should return a Content instance'(){ + public 'Repository.Parse should return a Content instance'() { const parsed = this._repo.ParseContent(this._contentSerializedString); expect(parsed).to.be.eq(this._content); } @test - public 'Repository.Parse should throw an Error when trying parse a Content from a different Repository origin'(){ - expect(() => {this._repo2.ParseContent(this._contentSerializedString)}).to.throw(); + public 'Repository.Parse should throw an Error when trying parse a Content from a different Repository origin'() { + expect(() => { this._repo2.ParseContent(this._contentSerializedString); }).to.throw(); } } diff --git a/test/ContentTests.ts b/test/ContentTests.ts index 4c1665e..5b27373 100644 --- a/test/ContentTests.ts +++ b/test/ContentTests.ts @@ -1,38 +1,39 @@ import * as Chai from 'chai'; -import { Observable } from '@reactivex/rxjs'; -import { MockRepository } from './Mocks'; +import { Observable } from 'rxjs/Observable'; import { LoginState } from '../src/Authentication/LoginState'; -import { isDeferred, isContentOptions, isContentOptionList, SavedContent, Content } from '../src/Content'; +import { MediaResourceObject } from '../src/ComplexTypes'; +import { Content, ContentInternal, ISavedContent, isContent, isDeferred, isIContent, isIContentList, SavedContent } from '../src/Content'; import { ContentReferenceField } from '../src/ContentReferences'; -import { Task, ITaskOptions, Workspace, User, GenericContent } from '../src/ContentTypes'; -import { joinPaths } from '../src/ODataHelper'; -import { PermissionValues, Inheritance, PermissionLevel, IdentityKind } from '../src/Security'; +import { File as SnFile, GenericContent, Task, User, Workspace } from '../src/ContentTypes'; import { QueryType } from '../src/Enums'; -import { Schema } from '../src/Schemas'; +import { joinPaths } from '../src/ODataHelper'; +import { isSchema } from '../src/Schemas'; +import { IdentityKind, Inheritance, PermissionLevel, PermissionValues } from '../src/Security'; +import { MockRepository } from './Mocks'; const expect = Chai.expect; const CONTENT_TYPE = 'Task'; const CONTENT_NAME = 'TestTask'; const CONTENT_DUE_TEXT = 'DueText'; - export const contentTests = describe('Content', () => { - let content: Task; + let content: Content; let contentSaved: SavedContent; let repo: MockRepository; beforeEach(() => { repo = new MockRepository(); - const options: ITaskOptions = { + const options: Task & ISavedContent = { Id: 1, Path: 'Root/Sites', DueDate: '2017-06-27T11:11:11Z', DueText: CONTENT_DUE_TEXT, Name: CONTENT_NAME, - DisplayName: '' + DisplayName: '', + Type: CONTENT_TYPE }; - content = Content.Create(options, Task, repo); - contentSaved = repo.HandleLoadedContent(options as any, Task); + content = repo.CreateContent(options, Task); + contentSaved = repo.HandleLoadedContent(options); repo.Authentication.StateSubject.next(LoginState.Authenticated); }); @@ -54,28 +55,28 @@ export const contentTests = describe('Content', () => { }); }); - describe('#isContentOptions ', () => { + describe('#isIContent ', () => { it('should return true if an object Id, Path and Type', () => { - const isContentOptionsValue = isContentOptions({ Id: 1, Path: 'a/b', Type: 'Task' }); + const isContentOptionsValue = isIContent({ Id: 1, Path: 'a/b', Type: 'Task' }); expect(isContentOptionsValue).to.be.eq(true); }); it('should return false if an object does not have a Type', () => { - const isContentOptionsValue = isContentOptions({ Id: 1, Path: 'a/b' }); + const isContentOptionsValue = isIContent({ Id: 1, Path: 'a/b' }); expect(isContentOptionsValue).to.be.eq(false); }); it('should return false if an object does not have a Path', () => { - const isContentOptionsValue = isContentOptions({ Id: 1, Type: 'Task' }); + const isContentOptionsValue = isIContent({ Id: 1, Type: 'Task' }); expect(isContentOptionsValue).to.be.eq(false); }); it('should return false if an object does not have an Id', () => { - const isContentOptionsValue = isContentOptions({ Path: 'a/b', Type: 'Task' }); + const isContentOptionsValue = isIContent({ Path: 'a/b', Type: 'Task' }); expect(isContentOptionsValue).to.be.eq(false); }); }); - describe('#isContentOptionList ', () => { + describe('#isIContentList ', () => { it('should return true if a list contains only values that have an object Id, Path and Type', () => { - const isContentOptionListValue = isContentOptionList([ + const isContentOptionListValue = isIContentList([ { Id: 1, Path: 'a/b', Type: 'Task' }, { Id: 2, Path: 'a/b/c', Type: 'Task' }, { Id: 3, Path: 'a/b/c/d', Type: 'Task' } @@ -83,16 +84,57 @@ export const contentTests = describe('Content', () => { expect(isContentOptionListValue).to.be.eq(true); }); it('should return false if an object does not have a length', () => { - const isContentOptionListValue = isContentOptionList(1 as any); + const isContentOptionListValue = isIContentList(1 as any); expect(isContentOptionListValue).to.be.eq(false); }); it('should return false if an object is not array-like', () => { - const isContentOptionListValue = isContentOptionList({ Path: 'a/b', Type: 'Task' } as any); + const isContentOptionListValue = isIContentList({ Path: 'a/b', Type: 'Task' } as any); expect(isContentOptionListValue).to.be.eq(false); }); }); + describe('#isIContentList ', () => { + it('should return false if an object is null or undefined', () => { + expect(isContent(null)).to.be.eq(false); + }); + it('should return false if an object Id is null or undefined', () => { + expect(isContent({Id: null})).to.be.eq(false); + }); + it('should return false if an object Path is null or undefined', () => { + expect(isContent({Path: null})).to.be.eq(false); + }); + it('should return false if an object Type is null or undefined', () => { + expect(isContent({Type: null})).to.be.eq(false); + }); + it('should return false if an object Type is zero-legth', () => { + expect(isContent({Type: ''})).to.be.eq(false); + }); + it('should return true for Content instances', () => { + expect(isContent(content)).to.be.eq(true); + }); + }); + + describe('#isSchema ', () => { + it('should return false for null / undefined', () => { + expect(isSchema(null as any)).to.be.eq(false); + }); + it('should return false for empty objects', () => { + expect(isSchema({} as any)).to.be.eq(false); + }); + it('should return false if ContentTypeName is missing', () => { + expect(isSchema({ContentTypeName: null} as any)).to.be.eq(false); + }); + it('should return false if FieldSettings are missing', () => { + expect(isSchema({ContentTypeName: 'Schema'} as any)).to.be.eq(false); + }); + it('should return false if FieldSettings are not array-like', () => { + expect(isSchema({ContentTypeName: 'Schema', FieldSettings: 1} as any)).to.be.eq(false); + }); + it('should return true for valid FieldSettings', () => { + expect(isSchema({ContentTypeName: 'Schema', FieldSettings: []} as any)).to.be.eq(true); + }); + }); }); @@ -102,23 +144,23 @@ export const contentTests = describe('Content', () => { expect(content).to.be.instanceof(Object); }); it('should return an instance of a Content', () => { - expect(content).to.be.instanceof(Content); - }) + expect(content).to.be.instanceof(ContentInternal); + }); it('should return an object with the given type and id', () => { const type = content.Type; expect(type).to.eq(CONTENT_TYPE); expect(content.Id).to.eq(1); }); it('should fill the Type field from the constructor name if not provided', () => { - let newContent = Content.Create({}, Content, repo); - expect(newContent.Type).to.be.eq('Content'); + const newContent = repo.CreateContent({}, GenericContent); + expect(newContent.Type).to.be.eq('GenericContent'); }); it('should have a valid Type field when constructed with new T(options)', () => { - let newContent = new Content({}, repo); - expect(newContent.Type).to.be.eq('Content'); + const newContent = new ContentInternal({}, repo, Task); + expect(newContent.Type).to.be.eq('Task'); }); it('shoul respect the type field, if provided from settings', () => { - let newContent = new Content({}, repo); + const newContent = new ContentInternal({Type: 'Task'}, repo, Task); newContent.Type = 'Task'; expect(newContent.Type).to.be.eq('Task'); }); @@ -141,8 +183,8 @@ export const contentTests = describe('Content', () => { expect(contentSaved).to.be.instanceof(Object); }); it('should return an instance of a Content', () => { - expect(contentSaved).to.be.instanceof(Content); - }) + expect(contentSaved).to.be.instanceof(ContentInternal); + }); it('should return an object with the given type and id', () => { const type = content.Type; expect(type).to.eq(CONTENT_TYPE); @@ -154,7 +196,7 @@ export const contentTests = describe('Content', () => { it('should have a list about Saved fields', () => { expect(contentSaved.SavedFields.DueText).to.be.eq(CONTENT_DUE_TEXT); - contentSaved.DueText = 'Modified' + contentSaved.DueText = 'Modified'; expect(contentSaved.SavedFields.DueText).to.be.eq(CONTENT_DUE_TEXT); }); }); @@ -167,8 +209,13 @@ export const contentTests = describe('Content', () => { content.Name = 'Modified DisplayName'; expect(content.IsDirty).to.be.eq(true); }); - }); + it('should skip binary fields', () => { + const file = repo.HandleLoadedContent({Id: 71253, Path: 'asas', Name: 'Asdasdasd', Binary: null as any}, SnFile); + file.Binary.SaveBinaryText('alma'); + expect(file.IsDirty).to.be.eq(false); + }); + }); describe('#GetChanges', () => { it('should return empty if the content is untouched', () => { @@ -191,21 +238,21 @@ export const contentTests = describe('Content', () => { Name: 'bbb', Path: 'Root/Workspace', Type: 'Workspace' - } + }; repo.HttpProviderRef.AddResponse({ d: options }); - contentSaved.ReloadFields('Workspace').subscribe(w => { - contentSaved.Workspace && contentSaved.Workspace.SetContent(repo.HandleLoadedContent({ + contentSaved.ReloadFields('Workspace').subscribe((w) => { + contentSaved.Workspace.SetContent(repo.HandleLoadedContent({ Id: 92635, Path: 'Root/MyWorkspace', Type: 'Workspace', Name: 'ExampleWorkspace' - }, Workspace)); + })); const changes = contentSaved.GetChanges(); expect(Object.keys(changes).length).to.be.eq(1); expect(changes.Workspace && changes.Workspace).to.be.eq('Root/MyWorkspace'); done(); - }, err => done) + }, (err) => done); }); it('should return an updated Path list if a reference list has been changed', (done) => { @@ -214,20 +261,32 @@ export const contentTests = describe('Content', () => { (options.Versions as any) = []; repo.HttpProviderRef.AddResponse({ d: options }); - contentSaved.ReloadFields('Versions').subscribe(w => { - contentSaved.Versions && contentSaved.Versions.SetContent([contentSaved]); + contentSaved.ReloadFields('Versions').subscribe((w) => { + contentSaved.Versions.SetContent([contentSaved]); const changes = contentSaved.GetChanges(); expect(Object.keys(changes).length).to.be.eq(1); expect(changes.Versions && (changes.Versions as any)[0]).to.be.eq(options.Path); done(); - }, err => done) + }, (err) => done); + }); + + it('should return a DownloadUrl for binary fields', () => { + const file = repo.HandleLoadedContent({ + Binary: { + __mediaresource: { + media_src: '/binaryhandler.ashx/test' + } + } as MediaResourceObject, + } as any, SnFile); + + expect(file.GetFields().Binary).to.be.eq('/binaryhandler.ashx/test'); }); }); describe('#IsValid', () => { it('should return false if there are missing fields', () => { - const emptyContent = Content.Create({}, Task, repo); + const emptyContent = ContentInternal.Create({}, Task, repo); expect(emptyContent.IsValid).to.be.eq(false); }); it('should return true all complusory fields are filled', () => { @@ -243,23 +302,23 @@ export const contentTests = describe('Content', () => { it('should trigger an OnContentDeleted event', (done) => { repo.Authentication.StateSubject.next(LoginState.Authenticated); repo.HttpProviderRef.AddResponse({}); - repo.Events.OnContentDeleted.subscribe(d => { + repo.Events.OnContentDeleted.subscribe((d) => { done(); - }, err => done(err)) + }, (err) => done(err)); expect(content.Delete(false)).to.be.instanceof(Observable); }); it('error should trigger an OnContentDeleteFailed event', (done) => { repo.Authentication.StateSubject.next(LoginState.Authenticated); repo.HttpProviderRef.AddError({}); - repo.Events.OnContentDeleteFailed.subscribe(d => { + repo.Events.OnContentDeleteFailed.subscribe((d) => { done(); - }, err => done(err)) + }, (err) => done(err)); expect(content.Delete(false)).to.be.instanceof(Observable); }); it('should return an Observable on not saved content', () => { - const unsavedContent = Content.Create({}, Task, repo); + const unsavedContent = ContentInternal.Create({}, Task, repo); expect(unsavedContent.Delete(false)).to.be.instanceof(Observable); }); }); @@ -270,9 +329,9 @@ export const contentTests = describe('Content', () => { DisplayName: 'aaa', Name: 'bbb' } - }) + }); - contentSaved.Rename('aaa', 'bbb').subscribe(result => { + contentSaved.Rename('aaa', 'bbb').subscribe((result) => { expect(result.DisplayName).to.be.eq('aaa'); expect(result.Name).to.be.eq('bbb'); done(); @@ -284,19 +343,19 @@ export const contentTests = describe('Content', () => { }); it('should throw an error if no ID provided', () => { - const newContent = Content.Create({}, Task, repo); - expect(() => { newContent.Rename('aaa', 'bbb') }).to.throw() + const newContent = ContentInternal.Create({}, Task, repo); + expect(() => { newContent.Rename('aaa', 'bbb'); }).to.throw(); }); it('should throw an error if trying to rename an unsaved content with Id', () => { - const newContent = Content.Create({ Id: 3 }, Task, repo); - expect(() => { newContent.Rename('aaa', 'bbb') }).to.throw() + const newContent = ContentInternal.Create({ Id: 3 }, Task, repo); + expect(() => { newContent.Rename('aaa', 'bbb'); }).to.throw(); }); }); describe('#Save()', () => { it('should throw an error if trying to update, but not saved in the Repository', () => { - expect(() => { content.Save({ DisplayName: 'new' }, true) }).to.throw() + expect(() => { content.Save({ DisplayName: 'new' }, true); }).to.throw(); }); it('should return an Observable object and isOperationInProgress should be updated during the operation', () => { @@ -311,29 +370,24 @@ export const contentTests = describe('Content', () => { }); it('should throw Error if no Id specified and isOperationInProgress should be updated during the operation', () => { - const emptyContent = Content.Create({}, Task, repo); + const emptyContent = ContentInternal.Create({}, Task, repo); expect(() => { - const obs = emptyContent.Save({ DisplayName: 'new' }) + const obs = emptyContent.Save({ DisplayName: 'new' }); obs.subscribe(() => { expect(emptyContent.IsOperationInProgress).to.be.eq(false); - }, e => { - - }) + }, (e) => { /** */ }); expect(emptyContent.IsOperationInProgress).to.be.eq(true); }).to.throw(); }); - it('should throw Error if no Id specified and isOperationInProgress should be updated during the operation', () => { - const savedContent = repo.HandleLoadedContent({ DisplayName: 'Original' } as any, Task); + const savedContent = repo.HandleLoadedContent({ DisplayName: 'Original' } as any); savedContent.DisplayName = 'Modified'; expect(() => { - const obs = savedContent.Save() + const obs = savedContent.Save(); obs.subscribe(() => { expect(savedContent.IsOperationInProgress).to.be.eq(false); - }, e => { - - }) + }, (e) => { /** */ }); expect(savedContent.IsOperationInProgress).to.be.eq(true); }).to.throw(); @@ -341,11 +395,11 @@ export const contentTests = describe('Content', () => { it('should throw Error is server returns Error, and isOperationInProgress should be set to False', (done) => { repo.HttpProviderRef.AddError({ message: 'serverErrorMessage' }); - let c = repo.HandleLoadedContent({ Id: 1, Path: 'Root/Test' }, Task); + const c = repo.HandleLoadedContent({ Id: 1, Path: 'Root/Test', Name: 'asd' }); - c.Save({ DisplayName: 'new' }).subscribe(resp => { + c.Save({ DisplayName: 'new' }).subscribe((resp) => { done('Error should be thrown here'); - }, err => { + }, (err) => { expect(c.IsOperationInProgress).to.be.eq(false); done(); }, done); @@ -357,9 +411,9 @@ export const contentTests = describe('Content', () => { DisplayName: 'new', } }); - let c = repo.HandleLoadedContent({ Id: 1, Path: 'Root/Test' }, Task); + const c = repo.HandleLoadedContent({ Id: 1, Path: 'Root/Test', Name: 'asd' }); - c.Save({ DisplayName: 'new' }).subscribe(resp => { + c.Save({ DisplayName: 'new' }).subscribe((resp) => { const lastOptions = repo.HttpProviderRef.LastOptions; expect(lastOptions.method).to.be.eq('PATCH'); expect(c.DisplayName).to.be.eq('new'); @@ -372,8 +426,8 @@ export const contentTests = describe('Content', () => { d: { DisplayName: 'new2', } - }) - contentSaved.Save({ DisplayName: 'new2' }, true).subscribe(resp => { + }); + contentSaved.Save({ DisplayName: 'new2' }, true).subscribe((resp) => { const lastOptions = repo.HttpProviderRef.RequestLog[repo.HttpProviderRef.RequestLog.length - 1].Options; expect(lastOptions.method).to.be.eq('PUT'); expect(contentSaved.DisplayName).to.be.eq('new2'); @@ -381,15 +435,14 @@ export const contentTests = describe('Content', () => { }); }); - it('should send a POST request if triggering Save on an unsaved Content', (done) => { repo.HttpProviderRef.AddResponse({ d: { Id: 3, DisplayName: 'new3', } - }) - content.Save().subscribe(resp => { + }); + content.Save().subscribe((resp) => { const lastOptions = repo.HttpProviderRef.RequestLog[repo.HttpProviderRef.RequestLog.length - 1].Options; expect(lastOptions.method).to.be.eq('POST'); expect(content.DisplayName).to.be.eq('new3'); @@ -397,10 +450,9 @@ export const contentTests = describe('Content', () => { }); }); - it('should throw error when triggering Save on an unsaved Content without path', () => { - let c = Content.Create({}, Task, repo); - expect(() => { c.Save() }).to.throw(); + const c = ContentInternal.Create({}, Task, repo); + expect(() => { c.Save(); }).to.throw(); }); it('should return an Observable without request on a non-dirty content', (done) => { @@ -408,25 +460,24 @@ export const contentTests = describe('Content', () => { d: { DisplayName: 'new3', } - }) - let c = repo.HandleLoadedContent({ DisplayName: 'test', Path: 'Root/Test', Id: 3845 }, Task); - c.Save().subscribe(modifiedContent => { + }); + const c = repo.HandleLoadedContent({ Id: 1, Path: 'Root/Test', Name: 'asd', DisplayName: 'test' }); + c.Save().subscribe((modifiedContent) => { expect(modifiedContent.DisplayName).to.be.eq('test'); done(); - }) + }); }); - it('should send a PATCH request if triggering Save on an already saved Content', (done) => { repo.HttpProviderRef.AddResponse({ d: { DisplayName: 'new3', } - }) + }); contentSaved.DisplayName = 'new3'; - contentSaved.Save().subscribe(resp => { + contentSaved.Save().subscribe((resp) => { const lastOptions = repo.HttpProviderRef.RequestLog[repo.HttpProviderRef.RequestLog.length - 1].Options; expect(lastOptions.method).to.be.eq('PATCH'); expect(contentSaved.DisplayName).to.be.eq('new3'); @@ -439,11 +490,11 @@ export const contentTests = describe('Content', () => { d: { DisplayName: 'new3', } - }) + }); content.Save().subscribe(() => { - done('This shouldn\'t happened') - }, err => { - expect(err.message).to.be.eq('Error: No content Id in response!') + done('This shouldn\'t happened'); + }, (err) => { + expect(err.message).to.be.eq('Error: No content Id in response!'); done(); }); }); @@ -454,8 +505,8 @@ export const contentTests = describe('Content', () => { Id: 3, DisplayName: 'new3', } - }) - repo.Events.OnContentCreated.subscribe(c => { + }); + repo.Events.OnContentCreated.subscribe((c) => { expect(c.Content.Id).to.be.eq(3); done(); }); @@ -465,8 +516,8 @@ export const contentTests = describe('Content', () => { it('should trigger an OnContentCreateFailed event on failure', (done) => { repo.HttpProviderRef.AddError({ message: ':(' - }) - repo.Events.OnContentCreateFailed.subscribe(c => { + }); + repo.Events.OnContentCreateFailed.subscribe((c) => { expect(c.Error.message).to.be.eq(':('); done(); }); @@ -479,37 +530,36 @@ export const contentTests = describe('Content', () => { Id: 3, DisplayName: 'new3', } - }) + }); - repo.Events.OnContentModified.subscribe(c => { + repo.Events.OnContentModified.subscribe((c) => { expect(c.Content.DisplayName).to.be.eq('new3'); done(); - }, err => done(err)); + }, (err) => done(err)); contentSaved.DisplayName = 'old'; contentSaved.Save(); }); it('should trigger an OnContentModificationFailed event on failed after update', (done) => { - repo.HttpProviderRef.AddError({ message: ':(' }) + repo.HttpProviderRef.AddError({ message: ':(' }); - repo.Events.OnContentModificationFailed.subscribe(c => { + repo.Events.OnContentModificationFailed.subscribe((c) => { expect(c.Error.message).to.be.eq(':('); done(); - }, err => done(err)); + }, (err) => done(err)); contentSaved.DisplayName = 'old'; contentSaved.Save(); }); - it('should trigger an OnContentModificationFailed event on failed after update, when using Override', (done) => { - repo.HttpProviderRef.AddError({ message: ':(' }) + repo.HttpProviderRef.AddError({ message: ':(' }); - repo.Events.OnContentModificationFailed.subscribe(c => { + repo.Events.OnContentModificationFailed.subscribe((c) => { expect(c.Error.message).to.be.eq(':('); done(); - }, err => done(err)); + }, (err) => done(err)); contentSaved.Save({ DisplayName: 'other' @@ -525,10 +575,10 @@ export const contentTests = describe('Content', () => { it('should throw an error if no Path specified', () => { (contentSaved as any).Path = undefined; - expect(() => {contentSaved.Upload('Root/Example', 'example.txt'); }).to.throw('No Path provided!') + expect(() => {contentSaved.Upload('Root/Example', 'example.txt'); }).to.throw('No Path provided!'); }); - }) + }); describe('#Actions()', () => { it('should return an Observable object', () => { @@ -546,7 +596,7 @@ export const contentTests = describe('Content', () => { ] } }); - contentSaved.Actions().subscribe(actions => { + contentSaved.Actions().subscribe((actions) => { expect(actions[0].Name).to.be.eq('Action1'); done(); }, done); @@ -574,10 +624,10 @@ export const contentTests = describe('Content', () => { ] } }); - contentSaved.GetAllowedChildTypes().subscribe(resp => { + contentSaved.GetAllowedChildTypes().subscribe((resp) => { expect(resp[0].Name).to.be.eq('MyCustomType1'); done(); - }) + }); }); it('should return an Observable object', () => { expect(content.GetAllowedChildTypes({ select: ['Name'] })).to.be.instanceof(Observable); @@ -641,8 +691,8 @@ export const contentTests = describe('Content', () => { }); it('should throw error if no path provided', () => { - const contentWithoutPath = repo.HandleLoadedContent({} as any, Task); - expect(() => { contentWithoutPath.Children() }).to.throw(); + const contentWithoutPath = repo.HandleLoadedContent({} as any); + expect(() => { contentWithoutPath.Children(); }).to.throw(); }); it('should be resolved with a list of content', (done) => { @@ -656,15 +706,14 @@ export const contentTests = describe('Content', () => { } ] } - }) + }); - content.Children().subscribe(children => { - expect(children[0]).to.be.instanceof(Content); + content.Children().subscribe((children) => { + expect(children[0]).to.be.instanceof(ContentInternal); done(); - }, done) + }, done); }); - }); describe('#Versions()', () => { it('should return an Observable object', () => { @@ -780,14 +829,14 @@ export const contentTests = describe('Content', () => { const newPath = joinPaths(toPath, contentSaved.Name || ''); repo.HttpProviderRef.AddResponse({}); - repo.Events.OnContentMoved.subscribe(move => { + repo.Events.OnContentMoved.subscribe((move) => { expect(move.From).to.be.eq(originalPath); expect(move.To).to.be.eq(toPath); expect(move.Content.Path).to.be.eq(newPath); expect(move.Content.SavedFields.Path).to.be.eq(newPath); done(); - }, err => done(err)); + }, (err) => done(err)); contentSaved.MoveTo(toPath); }); @@ -796,7 +845,7 @@ export const contentTests = describe('Content', () => { const toPath = 'workspaces/document'; repo.HttpProviderRef.AddError({ message: ':(' }); - repo.Events.OnContentMoveFailed.subscribe(move => { + repo.Events.OnContentMoveFailed.subscribe((move) => { expect(move.Error.message).to.be.eq(':('); expect(move.From).to.be.eq(originalPath); expect(move.To).to.be.eq(toPath); @@ -804,7 +853,7 @@ export const contentTests = describe('Content', () => { expect(move.Content.Path).to.be.eq(originalPath); expect(move.Content.SavedFields.Path).to.be.eq(originalPath); done(); - }, err => done(err)); + }, (err) => done(err)); contentSaved.MoveTo(toPath); }); @@ -826,8 +875,10 @@ export const contentTests = describe('Content', () => { }); describe('#HandleLoadedContent()', () => { - it('should return a ContentType object', () => { - expect(Content.HandleLoadedContent(Task, { Id: 1, Path: 'a/b' }, repo)).to.be.instanceof(Task); + it('should return a Content object with the specified generic field(s)', () => { + const loaded = repo.HandleLoadedContent({ Id: 1, Path: 'a/b', Name: 'a/b', DueText: 'test' }); + expect(loaded).to.be.instanceof(ContentInternal); + expect(loaded.DueText).to.be.eq('test'); }); }); @@ -851,12 +902,12 @@ export const contentTests = describe('Content', () => { }); it('should throw Error when called on a non-saved Content', () => { - expect(() => { content.Reload('view') }).to.throw('Content has to be saved to reload') + expect(() => { content.Reload('view'); }).to.throw('Content has to be saved to reload'); }); it('should throw Error when no Id provided', () => { - const invalidContent = repo.HandleLoadedContent({ Name: 'test' } as any, Task); - expect(() => { invalidContent.Reload('view') }).to.throw('Content Id or Path has to be provided') + const invalidContent = repo.HandleLoadedContent({ Name: 'test' } as any); + expect(() => { invalidContent.Reload('view'); }).to.throw('Content Id or Path has to be provided'); }); }); @@ -866,12 +917,12 @@ export const contentTests = describe('Content', () => { }); it('should throw Error when called on a non-saved Content', () => { - expect(() => { content.ReloadFields('Name') }).to.throw('Content has to be saved to reload') + expect(() => { content.ReloadFields('Name'); }).to.throw('Content has to be saved to reload'); }); it('should throw Error when no Id provided', () => { - const invalidContent = repo.HandleLoadedContent({ Name: 'Test' } as any, Task); - expect(() => { invalidContent.ReloadFields('Name') }).to.throw('Content Id or Path has to be provided') + const invalidContent = repo.HandleLoadedContent({ Name: 'Test' } as any); + expect(() => { invalidContent.ReloadFields('Name'); }).to.throw('Content Id or Path has to be provided'); }); it('should throw Error when no Id provided', (done) => { @@ -884,20 +935,19 @@ export const contentTests = describe('Content', () => { Name: 'bbb', Path: 'Root/Workspace', Type: 'Workspace' - } - repo.HttpProviderRef.AddResponse({ d: options }) - contentSaved.ReloadFields('Workspace').subscribe(c => { + }; + repo.HttpProviderRef.AddResponse({ d: options }); + contentSaved.ReloadFields('Workspace').subscribe((c) => { expect(contentSaved.Workspace).to.be.instanceof(ContentReferenceField); - contentSaved.Workspace && contentSaved.Workspace.GetContent().subscribe(c => { - expect(c.DisplayName).to.be.eq('aaa') + contentSaved.Workspace.GetContent().subscribe((loaded) => { + expect(loaded.DisplayName).to.be.eq('aaa'); done(); - }, done) - }, done) + }, done); + }, done); }); }); - describe('#SetPermissions()', () => { it('should return an Observable object', () => { expect(typeof content.SetPermissions([ @@ -985,7 +1035,7 @@ export const contentTests = describe('Content', () => { }); it('should return an Observable object', () => { - const usr = repo.HandleLoadedContent({ Path: 'Root/Users/alba', Id: 124798 }, User); + const usr = repo.HandleLoadedContent({ Path: 'Root/Users/alba', Id: 124798, Name: 'Name' }); expect(contentSaved.HasPermission(['AddNew', 'Save'], usr)).to.be.instanceof(Observable); }); }); @@ -1085,47 +1135,28 @@ export const contentTests = describe('Content', () => { describe('#GetSchema()', () => { it('should return a Schema object', () => { - expect(content.GetSchema()).to.be.instanceof(Schema); + expect(isSchema(content.GetSchema())).to.be.eq(true); }); it('should return a Task', () => { const schema = content.GetSchema(); expect(schema.Icon).to.eq('FormItem'); }); it('should return GenericContent Schema if no Schema found', () => { - class ContentWithoutSchema extends Content { }; - const contentInstance = Content.Create({}, ContentWithoutSchema, repo); - const genericSchema = Content.GetSchema(GenericContent); - expect(contentInstance.GetSchema()).to.be.eq(genericSchema) + class ContentWithoutSchema extends Task { } + const contentInstance = ContentInternal.Create({}, ContentWithoutSchema, repo); + const genericSchema = repo.GetSchema(GenericContent); + expect(contentInstance.GetSchema()).to.be.deep.eq(genericSchema); }); - - }); - describe('#static GetSchema()', () => { - it('should return a Schema object', () => { - expect(Content.GetSchema(Task)).to.be.instanceof(Schema); - }); - it('should return a Schema object', () => { - let schema = Content.GetSchema(Task) - expect(schema.Icon).to.eq('FormItem'); - }); - }); - describe('#Schema()', () => { - it('should return a Schema object', () => { - expect(content.GetSchema()).to.be.instanceof(Schema); - }); - it('should return a Schema object', () => { - let schema = content.GetSchema() - expect(schema.Icon).to.eq('FormItem'); - }); }); describe('#ParentPath', () => { it('should throw Error if no Path is provided', () => { const contentWithoutPath = repo.HandleLoadedContent({} as any); - expect(() => { return contentWithoutPath.ParentPath; }).to.throw(); + expect(() => contentWithoutPath.ParentPath).to.throw(); }); it('should throw Error if a Content is not saved', () => { - expect(() => { return content.ParentPath; }).to.throw(); + expect(() => content.ParentPath).to.throw(); }); it('should return a Path without a last segment', () => { expect(contentSaved.ParentPath).to.eq('Root'); @@ -1134,12 +1165,12 @@ export const contentTests = describe('Content', () => { describe('#IsParentOf', () => { - const repo = new MockRepository(); + const repository = new MockRepository(); - const rootContent = repo.HandleLoadedContent({ Path: 'Root', Id: 3 }); - const childContent = repo.HandleLoadedContent({ Path: 'Root/Child', Id: 976235 }); - const childContentById = repo.HandleLoadedContent({ ParentId: 3 } as any); - const notChildContent = repo.HandleLoadedContent({ Path: 'NotRoot/Child', Id: 973256 }); + const rootContent = repository.HandleLoadedContent({ Path: 'Root', Id: 3, Name: 'Test' }); + const childContent = repository.HandleLoadedContent({ Path: 'Root/Child', Id: 976235, Name: 'Test' }); + const childContentById = repository.HandleLoadedContent({ ParentId: 3 } as any); + const notChildContent = repository.HandleLoadedContent({ Path: 'NotRoot/Child', Id: 973256, Name: 'Test' }); it('should return true if content is a child', () => { expect(rootContent.IsParentOf(childContent)).to.be.eq(true); @@ -1155,20 +1186,19 @@ export const contentTests = describe('Content', () => { expect(content.IsParentOf(childContent)).to.be.eq(false); }); it('should return false on repository mismatch', () => { - const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 97256 }); + const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 97256, Name: 'Test' }); expect(rootContent.IsParentOf(otherChild)).to.be.eq(false); }); }); - describe('#IsChildOf', () => { - const repo = new MockRepository(); + const repository = new MockRepository(); - const rootContent = repo.HandleLoadedContent({ Path: 'Root', Id: 33245 }); - const childContent = repo.HandleLoadedContent({ Path: 'Root/Child', Id: 23545 }); - const childContentById = repo.HandleLoadedContent({ ParentId: 33245, Id: 23597 } as any); - const notChildContent = repo.HandleLoadedContent({ Path: 'NotRoot/Child', Id: 235786 }); + const rootContent = repository.HandleLoadedContent({ Path: 'Root', Id: 33245, Name: 'Test' }); + const childContent = repository.HandleLoadedContent({ Path: 'Root/Child', Id: 23545, Name: 'Test' }); + const childContentById = repository.HandleLoadedContent({ ParentId: 33245, Id: 23597 } as any); + const notChildContent = repository.HandleLoadedContent({ Path: 'NotRoot/Child', Id: 235786, Name: 'Test' }); it('should return true if content is a child', () => { expect(childContent.IsChildOf(rootContent)).to.be.eq(true); @@ -1187,19 +1217,18 @@ export const contentTests = describe('Content', () => { }); it('should return false on repository mismatch', () => { - const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 235876 }); + const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 235876, Name: 'Test' }); expect(otherChild.IsChildOf(rootContent)).to.be.eq(false); }); }); - describe('#IsAncestorOf', () => { - const repo = new MockRepository(); + const repository = new MockRepository(); - const ancestor = repo.HandleLoadedContent({ Path: 'Root', Id: 232345 }); - const descendant = repo.HandleLoadedContent({ Path: 'Root/test/test2/Child', Id: 43275 }); - const notDescendant = repo.HandleLoadedContent({ Path: 'Root2/Child', Id: 2358967 }); + const ancestor = repository.HandleLoadedContent({ Path: 'Root', Id: 232345, Name: 'Test' }); + const descendant = repository.HandleLoadedContent({ Path: 'Root/test/test2/Child', Id: 43275, Name: 'Test' }); + const notDescendant = repository.HandleLoadedContent({ Path: 'Root2/Child', Id: 2358967, Name: 'Test' }); it('should return true if content is an ancestor', () => { expect(ancestor.IsAncestorOf(descendant)).to.be.eq(true); @@ -1213,27 +1242,26 @@ export const contentTests = describe('Content', () => { }); it('should return false on repository mismatch', () => { - const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 4562 }); + const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 4562, Name: 'Test' }); expect(ancestor.IsAncestorOf(otherChild)).to.be.eq(false); }); it('should throw an error if no ancestor path provided', () => { - const ancestorWithoutPath = repo.HandleLoadedContent({} as any); - expect(() => { return ancestorWithoutPath.IsAncestorOf(descendant) }).to.throw(); + const ancestorWithoutPath = repository.HandleLoadedContent({} as any); + expect(() => ancestorWithoutPath.IsAncestorOf(descendant)).to.throw(); }); it('should throw an error if no descendant path provided', () => { - const descendantWithoutPath = repo.HandleLoadedContent({} as any); - expect(() => { return ancestor.IsAncestorOf(descendantWithoutPath) }).to.throw(); + const descendantWithoutPath = repository.HandleLoadedContent({} as any); + expect(() => ancestor.IsAncestorOf(descendantWithoutPath)).to.throw(); }); }); - describe('#IsDescendantOf', () => { - const repo = new MockRepository(); + const repository = new MockRepository(); - const ancestor = repo.HandleLoadedContent({ Path: 'Root', Id: 12314 }); - const descendant = repo.HandleLoadedContent({ Path: 'Root/test/test2/Child', Id: 212314 }); - const notAncestor = repo.HandleLoadedContent({ Path: 'Root2', Id: 124314 }); + const ancestor = repository.HandleLoadedContent({ Path: 'Root', Id: 12314, Name: 'Test' }); + const descendant = repository.HandleLoadedContent({ Path: 'Root/test/test2/Child', Id: 212314, Name: 'Test' }); + const notAncestor = repository.HandleLoadedContent({ Path: 'Root2', Id: 124314, Name: 'Test' }); it('should return true if content is an ancestor', () => { expect(descendant.IsDescendantOf(ancestor)).to.be.eq(true); @@ -1247,16 +1275,16 @@ export const contentTests = describe('Content', () => { }); it('should return false on repository mismatch', () => { - const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 123 }); + const otherChild = new MockRepository().HandleLoadedContent({ Path: 'Root/Child', Id: 123, Name: 'Test' }); expect(ancestor.IsDescendantOf(otherChild)).to.be.eq(false); }); it('should throw an error if no ancestor path provided', () => { - const ancestorWithoutPath = repo.HandleLoadedContent({} as any); - expect(() => { return descendant.IsDescendantOf(ancestorWithoutPath) }).to.throw(); + const ancestorWithoutPath = repository.HandleLoadedContent({} as any); + expect(() => descendant.IsDescendantOf(ancestorWithoutPath)).to.throw(); }); it('should throw an error if no descendant path provided', () => { - const descendantWithoutPath = repo.HandleLoadedContent({} as any); - expect(() => { return descendantWithoutPath.IsDescendantOf(ancestor) }).to.throw(); + const descendantWithoutPath = repository.HandleLoadedContent({} as any); + expect(() => descendantWithoutPath.IsDescendantOf(ancestor)).to.throw(); }); }); @@ -1264,16 +1292,16 @@ export const contentTests = describe('Content', () => { it('should throw if Content is not saved', () => { expect(() => { content.GetFullPath(); - }).to.throw('Content has to be saved to get the full Path') + }).to.throw('Content has to be saved to get the full Path'); }); it('should throw if Content has no Id AND Path', () => { const c = repo.HandleLoadedContent({ Name: 'Test' - } as any) + } as any); expect(() => { c.GetFullPath(); - }).to.throw('Content Id or Path has to be provided to get the full Path') + }).to.throw('Content Id or Path has to be provided to get the full Path'); }); it('should return by Id if possible', () => { @@ -1281,18 +1309,17 @@ export const contentTests = describe('Content', () => { Name: 'Test', Id: 1, Path: 'Root/Test' - }) + }); expect(c.GetFullPath()).to.be.eq('/content(1)'); }); - it('should return by Path if Id is not available possible', () => { const c = repo.HandleLoadedContent({ Name: 'Test', Path: 'Root/Test' - } as any) + } as any); expect(c.GetFullPath()).to.be.eq("Root('Test')"); }); - }) + }); -}); \ No newline at end of file +}); diff --git a/test/ContentTypeTests.ts b/test/ContentTypeTests.ts deleted file mode 100644 index 29201f9..0000000 --- a/test/ContentTypeTests.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { ContentTypes } from '../src/SN'; -import * as Chai from 'chai'; -import { MockRepository } from './Mocks/MockRepository'; -const expect = Chai.expect; - -describe('ContentTypes', () => { - - - let repo = new MockRepository(); - - describe('#ContentType', () => { - const gc = new ContentTypes.ContentType({ Id: 1, Type: 'ContentType', Name: '' }, repo); - it('should return a ContentType type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.ContentType); - }); - }); - describe('#GenericContent', () => { - const gc = new ContentTypes.GenericContent({ Id: 1, Type: 'GenericContent', Name: '' }, repo); - it('should return a GenericContent type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.GenericContent); - }); - }); - describe('#Folder', () => { - const gc = new ContentTypes.Folder({ Id: 1, Type: 'Folder', Name: '' }, repo); - it('should return a Folder type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Folder); - }); - }); - describe('#ContentList', () => { - const gc = new ContentTypes.ContentList({ Id: 1, Type: 'ContentList', Name: '' }, repo); - it('should return a ContentList type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.ContentList); - }); - }); - describe('#Aspect', () => { - const gc = new ContentTypes.Aspect({ Id: 1, Type: 'Aspect', Name: '' }, repo); - it('should return a Aspect type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Aspect); - }); - }); - describe('#ItemList', () => { - const gc = new ContentTypes.ItemList({ Id: 1, Type: 'ItemList', Name: '' }, repo); - it('should return a ItemList type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.ItemList); - }); - }); - describe('#CustomList', () => { - const gc = new ContentTypes.CustomList({ Id: 1, Type: 'CustomList', Name: '' }, repo); - it('should return a CustomList type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.CustomList); - }); - }); - describe('#MemoList', () => { - const gc = new ContentTypes.MemoList({ Id: 1, Type: 'MemoList', Name: '' }, repo); - it('should return a MemoList type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.MemoList); - }); - }); - describe('#TaskList', () => { - const gc = new ContentTypes.TaskList({ Id: 1, Type: 'TaskList', Name: '' }, repo); - it('should return a TaskList type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.TaskList); - }); - }); - describe('#Library', () => { - const gc = new ContentTypes.Library({ Id: 1, Type: 'Library', Name: '' }, repo); - it('should return a Library type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Library); - }); - }); - describe('#DocumentLibrary', () => { - const gc = new ContentTypes.DocumentLibrary({ Id: 1, Type: 'DocumentLibrary', Name: '' }, repo); - it('should return a DocumentLibrary type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.DocumentLibrary); - }); - }); - describe('#ImageLibrary', () => { - const gc = new ContentTypes.ImageLibrary({ Id: 1, Type: 'ImageLibrary', Name: '' }, repo); - it('should return a ImageLibrary type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.ImageLibrary); - }); - }); - describe('#Workspace', () => { - const gc = new ContentTypes.Workspace({ Id: 1, Type: 'Workspace', Name: '', IsActive: false }, repo); - it('should return a Workspace type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Workspace); - }); - }); - describe('#Site', () => { - const gc = new ContentTypes.Site({ Id: 1, Type: 'Site', Name: '', IsActive: false }, repo); - it('should return a Site type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Site); - }); - }); - describe('#TrashBin', () => { - const gc = new ContentTypes.TrashBin({ Id: 1, Type: 'TrashBin', Name: '', IsActive: false }, repo); - it('should return a TrashBin type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.TrashBin); - }); - }); - describe('#SmartFolder', () => { - const gc = new ContentTypes.SmartFolder({ Id: 1, Type: 'SmartFolder', Name: '' }, repo); - it('should return a SmartFolder type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.SmartFolder); - }); - }); - describe('#Device', () => { - const gc = new ContentTypes.Device({ Id: 1, Type: 'Device', Name: '' }, repo); - it('should return a Device type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Device); - }); - }); - describe('#Domain', () => { - const gc = new ContentTypes.Domain({ Id: 1, Type: 'Domain', Name: '' }, repo); - it('should return a Domain type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Domain); - }); - }); - describe('#Domains', () => { - const gc = new ContentTypes.Domains({ Id: 1, Type: 'Domains', Name: '' }, repo); - it('should return a Domains type object', () => { - expect(gc).to.be.an.instanceof(ContentTypes.Domains); - }); - }); - describe('#Email', () => { - const content = new ContentTypes.Email({ Id: 1, Type: 'Email', Name: '' }, repo); - it('should return a Email type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Email); - }); - }); - describe('#OrganizationalUnit', () => { - const content = new ContentTypes.OrganizationalUnit({ Id: 1, Type: 'OrganizationalUnit', Name: '' }, repo); - it('should return a OrganizationalUnit type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.OrganizationalUnit); - }); - }); - describe('#PortalRoot', () => { - const content = new ContentTypes.PortalRoot({ Id: 1, Type: 'PortalRoot', Name: '' }, repo); - it('should return a PortalRoot type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.PortalRoot); - }); - }); - describe('#SystemFolder', () => { - const content = new ContentTypes.SystemFolder({ Id: 1, Type: 'SystemFolder', Name: '' }, repo); - it('should return a SystemFolder type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.SystemFolder); - }); - }); - describe('#Resources', () => { - const content = new ContentTypes.Resources({ Id: 1, Type: 'Resources', Name: '' }, repo); - it('should return a Resources type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Resources); - }); - }); - describe('#ProfileDomain', () => { - const content = new ContentTypes.ProfileDomain({ Id: 1, Type: 'ProfileDomain', Name: '' }, repo); - it('should return a ProfileDomain type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.ProfileDomain); - }); - }); - describe('#Profiles', () => { - const content = new ContentTypes.Profiles({ Id: 1, Type: 'Profiles', Name: '' }, repo); - it('should return a Profiles type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Profiles); - }); - }); - describe('#RuntimeContentContainer', () => { - const content = new ContentTypes.RuntimeContentContainer({ Id: 1, Type: 'RuntimeContentContainer', Name: '' }, repo); - it('should return a RuntimeContentContainer type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.RuntimeContentContainer); - }); - }); - describe('#Sites', () => { - const content = new ContentTypes.Sites({ Id: 1, Type: 'Sites', Name: '' }, repo); - it('should return a Sites type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Sites); - }); - }); - describe('#TrashBag', () => { - const content = new ContentTypes.TrashBag({ Id: 1, Type: 'TrashBag', Name: '' }, repo); - it('should return a TrashBag type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.TrashBag); - }); - }); - describe('#File', () => { - const content = new ContentTypes.File({ Id: 1, Type: 'File', Name: '' }, repo); - it('should return a File type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.File); - }); - }); - describe('#Settings', () => { - const content = new ContentTypes.Settings({ Id: 1, Type: 'Settings', Name: '' }, repo); - it('should return a Settings type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Settings); - }); - }); - describe('#IndexingSettings', () => { - const content = new ContentTypes.IndexingSettings({ Id: 1, Type: 'IndexingSettings', Name: '' }, repo); - it('should return a IndexingSettings type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.IndexingSettings); - }); - }); - describe('#LoggingSettings', () => { - const content = new ContentTypes.LoggingSettings({ Id: 1, Type: 'LoggingSettings', Name: '' }, repo); - it('should return a LoggingSettings type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.LoggingSettings); - }); - }); - describe('#PortalSettings', () => { - const content = new ContentTypes.PortalSettings({ Id: 1, Type: 'PortalSettings', Name: '' }, repo); - it('should return a PortalSettings type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.PortalSettings); - }); - }); - describe('#SystemFile', () => { - const content = new ContentTypes.SystemFile({ Id: 1, Type: 'SystemFile', Name: '' }, repo); - it('should return a SystemFile type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.SystemFile); - }); - }); - describe('#Resource', () => { - const content = new ContentTypes.Resource({ Id: 1, Type: 'Resource', Name: '' }, repo); - it('should return a Resource type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Resource); - }); - }); - describe('#DynamicJsonContent', () => { - const content = new ContentTypes.DynamicJsonContent({ Id: 1, Type: 'DynamicJsonContent', Name: '' }, repo); - it('should return a DynamicJsonContent type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.DynamicJsonContent); - }); - }); - describe('#ExecutableFile', () => { - const content = new ContentTypes.ExecutableFile({ Id: 1, Type: 'ExecutableFile', Name: '' }, repo); - it('should return a ExecutableFile type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.ExecutableFile); - }); - }); - describe('#HtmlTemplate', () => { - const content = new ContentTypes.HtmlTemplate({ Id: 1, Type: 'HtmlTemplate', Name: '' }, repo); - it('should return a HtmlTemplate type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.HtmlTemplate); - }); - }); - describe('#Image', () => { - const content = new ContentTypes.Image({ Id: 1, Type: 'Image', Name: '' }, repo); - it('should return a Image type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.Image); - }); - }); - describe('#PreviewImage', () => { - const content = new ContentTypes.PreviewImage({ Id: 1, Type: 'PreviewImage', Name: '' }, repo); - it('should return a PreviewImage type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.PreviewImage); - }); - }); - describe('#UserProfile', () => { - const content = new ContentTypes.UserProfile({ Id: 1, Type: 'UserProfile', Name: '', IsActive: false }, repo); - it('should return a UserProfile type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.UserProfile); - }); - }); - describe('#ListItem', () => { - const content = new ContentTypes.ListItem({ Id: 1, Type: 'ListItem', Name: '' }, repo); - it('should return a ListItem type object', () => { - expect(content).to.be.an.instanceof(ContentTypes.ListItem); - }); - }); - describe('#Task', () => { - const task = new ContentTypes.Task({ Id: 1, Type: 'Task', Name: '', DueDate: '2020-06-27T11:11:11Z' }, repo); - it('should return a Task type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.Task); - }); - }); - describe('#CustomListItem', () => { - const task = new ContentTypes.CustomListItem({ Id: 1, Type: 'CustomListItem', Name: '' }, repo); - it('should return a CustomListItem type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.CustomListItem); - }); - }); - describe('#Memo', () => { - const task = new ContentTypes.Memo({ Id: 1, Type: 'Memo', Name: '' }, repo); - it('should return a Memo type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.Memo); - }); - }); - describe('#ContentLink', () => { - const task = new ContentTypes.ContentLink({ Id: 1, Type: 'ContentLink', Name: '' }, repo); - it('should return a ContentLink type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.ContentLink); - }); - }); - describe('#Group', () => { - const task = new ContentTypes.Group({ Id: 1, Type: 'Group', Name: '' }, repo); - it('should return a Group type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.Group); - }); - }); - describe('#Query', () => { - const task = new ContentTypes.Query({ Id: 1, Type: 'Query', Name: '' }, repo); - it('should return a Query type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.Query); - }); - }); - describe('#User', () => { - const task = new ContentTypes.User({ Id: 1, Type: 'User', Name: '', LoginName: '', Email: '', FullName: '', Password: '' }, repo); - it('should return a User type object', () => { - expect(task).to.be.an.instanceof(ContentTypes.User); - }); - }); -}); \ No newline at end of file diff --git a/test/ControlMapperTests.ts b/test/ControlMapperTests.ts index 31f394b..60f75be 100644 --- a/test/ControlMapperTests.ts +++ b/test/ControlMapperTests.ts @@ -1,13 +1,12 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { ControlMapper } from '../src/ControlMapper'; -import { FieldSetting, ChoiceFieldSetting, ShortTextFieldSetting, FieldVisibility } from '../src/FieldSettings'; import { Task, User } from '../src/ContentTypes'; import * as ContentTypes from '../src/ContentTypes'; -import { Content } from '../src/Content'; +import { ControlMapper } from '../src/ControlMapper'; +import { ChoiceFieldSetting, FieldSetting, FieldVisibility, ShortTextFieldSetting } from '../src/FieldSettings'; +import { ContentInternal } from '../src/SN'; import { MockRepository } from './Mocks/MockRepository'; - class ExampleControlBase { } class ExampleDefaultControl extends ExampleControlBase { } @@ -19,7 +18,7 @@ class ExampleModifiedControl2 extends ExampleControlBase { } class ExampleDefaultFieldControl extends ExampleControlBase { } class ExampleClientSetting { - constructor(public readonly FieldSetting: FieldSetting) { + constructor(public readonly Setting: FieldSetting) { } } @@ -27,8 +26,11 @@ class ExampleClientSetting { @suite('ControlMapper') export class ControlMapperTests { private _mapper: ControlMapper; - before() { - this._mapper = new ControlMapper(ExampleControlBase, setting => new ExampleClientSetting(setting), ExampleDefaultControl, ExampleDefaultFieldControl); + private _repository: MockRepository; + // tslint:disable-next-line:naming-convention + public before() { + this._repository = new MockRepository(); + this._mapper = new ControlMapper(this._repository, ExampleControlBase, (setting) => new ExampleClientSetting(setting), ExampleDefaultControl, ExampleDefaultFieldControl); } @test @@ -38,13 +40,13 @@ export class ControlMapperTests { @test public 'Should be able to construct with BaseType and ClientControlSettingsFactory'() { - const mapper = new ControlMapper(ExampleControlBase, setting => new ExampleClientSetting(setting)); + const mapper = new ControlMapper(this._repository, ExampleControlBase, (setting) => new ExampleClientSetting(setting)); Chai.expect(mapper).to.be.instanceof(ControlMapper); } @test public 'Should be able to construct with all parameters'() { - const mapper = new ControlMapper(ExampleControlBase, setting => new ExampleClientSetting(setting), ExampleDefaultControl, ExampleDefaultFieldControl); + const mapper = new ControlMapper(this._repository, ExampleControlBase, (setting) => new ExampleClientSetting(setting), ExampleDefaultControl, ExampleDefaultFieldControl); Chai.expect(mapper).to.be.instanceof(ControlMapper); } @@ -63,7 +65,7 @@ export class ControlMapperTests { @test public 'Should return correct Default Control for FieldSettings'() { - const fs = new ChoiceFieldSetting({}); + const fs = {} as ChoiceFieldSetting; const controlType = this._mapper.GetControlForFieldSetting(fs); Chai.expect(controlType).to.be.eq(ExampleDefaultFieldControl); } @@ -78,17 +80,15 @@ export class ControlMapperTests { return ExampleDefaultFieldControl; }); - const fs = new ChoiceFieldSetting({ compulsory: true }); + const fs = { Compulsory: true, Type: 'ChoiceFieldSetting' } as ChoiceFieldSetting; const controlType = this._mapper.GetControlForFieldSetting(fs); Chai.expect(controlType).to.be.eq(ExampleModifiedControl); - const fs2 = new ChoiceFieldSetting({ compulsory: false }); + const fs2 = { Compulsory: false, Type: 'ChoiceFieldSetting' } as ChoiceFieldSetting; const controlType2 = this._mapper.GetControlForFieldSetting(fs2); Chai.expect(controlType2).to.be.eq(ExampleDefaultFieldControl); } - - @test public 'Should return a correct default control for a specified Content Field'() { const control = this._mapper.GetControlForContentField(Task, 'DisplayName', 'new'); @@ -99,7 +99,7 @@ export class ControlMapperTests { public 'Should return a correct default control for a specified Content Field when FieldSetting has default value'() { this._mapper.SetupFieldSettingDefault(ShortTextFieldSetting, (setting) => { return ExampleModifiedControl; - }) + }); const control = this._mapper.GetControlForContentField(Task, 'DisplayName', 'new'); Chai.expect(control).to.be.eq(ExampleModifiedControl); @@ -115,7 +115,7 @@ export class ControlMapperTests { public 'Should return a correct default control for a specified Content Field when there is a ContentType bound setting specified'() { this._mapper.SetupFieldSettingForControl(Task, 'DisplayName', (setting) => { return ExampleModifiedControl2; - }) + }); const control = this._mapper.GetControlForContentField(Task, 'DisplayName', 'new'); Chai.expect(control).to.be.eq(ExampleModifiedControl2); @@ -125,32 +125,33 @@ export class ControlMapperTests { @test public 'CreateClientSetting should run with defult factory method by default'() { - const fieldSetting = new ShortTextFieldSetting({ displayName: 'TestField' }); + const fieldSetting = { DisplayName: 'TestField' } as ShortTextFieldSetting; const clientSetting = this._mapper.CreateClientSetting(fieldSetting); - Chai.expect(clientSetting.FieldSetting.DisplayName).to.be.eq(fieldSetting.DisplayName); + Chai.expect(clientSetting.Setting.DisplayName).to.be.eq(fieldSetting.DisplayName); } @test public 'CreateClientSetting should be able to run with an overridden factory method'() { - const fieldSetting = new ShortTextFieldSetting({ displayName: 'TestField' }); - this._mapper.SetClientControlFactory(ShortTextFieldSetting, (setting => { - setting.DisplayName = (setting.DisplayName || '').toUpperCase() + const fieldSetting = { DisplayName: 'TestField', Type: 'ShortTextFieldSetting' } as ShortTextFieldSetting; + this._mapper.SetClientControlFactory(ShortTextFieldSetting, ((setting) => { + setting.DisplayName = (setting.DisplayName || '').toUpperCase(); return new ExampleClientSetting(setting); - })) + })); const clientSetting = this._mapper.CreateClientSetting(fieldSetting); - Chai.expect(clientSetting.FieldSetting.DisplayName).to.be.eq('TESTFIELD'); + Chai.expect(clientSetting.Setting.DisplayName).to.be.eq('TESTFIELD'); } @test public 'GetAllMappingsForContentTye should be able to return all mappings'() { - for (let key in ContentTypes) { + // tslint:disable-next-line:forin + for (const key in ContentTypes) { const fullMapping = this._mapper.GetFullSchemaForContentType((ContentTypes as any)[key], 'new'); Chai.expect(fullMapping.FieldMappings.length).to.be.greaterThan(0); - fullMapping.FieldMappings.forEach(m => { + fullMapping.FieldMappings.forEach((m) => { Chai.expect(m.ClientSettings).to.be.instanceof(ExampleClientSetting); Chai.expect(m.ControlType).to.be.eq(ExampleDefaultFieldControl); - }) + }); } } @@ -158,45 +159,43 @@ export class ControlMapperTests { public 'GetAllMappingsForContentTye filtered to View should be able to return all mappings'() { const fullMapping = this._mapper.GetFullSchemaForContentType(Task, 'view').FieldMappings; Chai.expect(fullMapping.length).to.be.greaterThan(0); - fullMapping.forEach(m => { - Chai.expect(m.ClientSettings.FieldSetting.VisibleBrowse).to.be.eq(FieldVisibility.Show); + fullMapping.forEach((m) => { + Chai.expect(m.ClientSettings.Setting.VisibleBrowse).to.be.eq(FieldVisibility.Show); Chai.expect(m.ClientSettings).to.be.instanceof(ExampleClientSetting); Chai.expect(m.ControlType).to.be.eq(ExampleDefaultFieldControl); - }) + }); } @test public 'GetAllMappingsForContentTye filtered to Edit should be able to return all mappings'() { const fullMapping = this._mapper.GetFullSchemaForContentType(Task, 'edit').FieldMappings; Chai.expect(fullMapping.length).to.be.greaterThan(0); - fullMapping.forEach(m => { - Chai.expect(m.ClientSettings.FieldSetting.VisibleEdit).to.be.eq(FieldVisibility.Show); + fullMapping.forEach((m) => { + Chai.expect(m.ClientSettings.Setting.VisibleEdit).to.be.eq(FieldVisibility.Show); Chai.expect(m.ClientSettings).to.be.instanceof(ExampleClientSetting); Chai.expect(m.ControlType).to.be.eq(ExampleDefaultFieldControl); - }) + }); } - @test public 'GetAllMappingsForContentTye filtered to New should be able to return all mappings'() { const fullMapping = this._mapper.GetFullSchemaForContentType(Task, 'new').FieldMappings; Chai.expect(fullMapping.length).to.be.greaterThan(0); - fullMapping.forEach(m => { - Chai.expect(m.ClientSettings.FieldSetting.VisibleNew).to.be.eq(FieldVisibility.Show); + fullMapping.forEach((m) => { + Chai.expect(m.ClientSettings.Setting.VisibleNew).to.be.eq(FieldVisibility.Show); Chai.expect(m.ClientSettings).to.be.instanceof(ExampleClientSetting); Chai.expect(m.ControlType).to.be.eq(ExampleDefaultFieldControl); - }) + }); } @test public 'GetFullSchemaForContent filtered to New should be able to return all mappings'() { - const fullMapping = this._mapper.GetFullSchemaForContent(Content.Create({DueDate: '2017-06-27T11:11:11Z', Name: 'Task1'}, Task, new MockRepository()), 'new').FieldMappings; + const fullMapping = this._mapper.GetFullSchemaForContent(ContentInternal.Create({DueDate: '2017-06-27T11:11:11Z', Name: 'Task1'}, Task, new MockRepository()), 'new').FieldMappings; Chai.expect(fullMapping.length).to.be.greaterThan(0); - fullMapping.forEach(m => { + fullMapping.forEach((m) => { Chai.expect(m.ClientSettings).to.be.instanceof(ExampleClientSetting); Chai.expect(m.ControlType).to.be.eq(ExampleDefaultFieldControl); - }) + }); } - -} \ No newline at end of file +} diff --git a/test/FieldSettingsTest.ts b/test/FieldSettingsTest.ts index be15a56..889a2b5 100644 --- a/test/FieldSettingsTest.ts +++ b/test/FieldSettingsTest.ts @@ -1,86 +1,17 @@ -import { FieldSettings } from '../src/SN' import * as Chai from 'chai'; +import * as FieldSettings from '../src/FieldSettings'; const expect = Chai.expect; -describe('FieldSettings', () => { - describe('#FieldSetting constructor', () => { +export const FieldSettingsTests = describe('FieldSettings', () => { + describe('#FieldSetting type guard', () => { let fieldSetting: FieldSettings.FieldSetting; beforeEach(() => { - fieldSetting = new FieldSettings.FieldSetting({ name: 'ShortText' }); + fieldSetting = { Name: 'ShortText', Type: 'ShortTextFieldSetting' } as FieldSettings.FieldSetting; }); - it('should return a FieldSetting object', () => { - expect(fieldSetting).to.be.an.instanceof(FieldSettings.FieldSetting); - }); - it('should return a FieldSetting object with a name ShortText', () => { - expect(fieldSetting.Name).to.be.eq('ShortText'); - }); - it('should return null as the Icon of the FieldSetting object', () => { - let inst = new FieldSettings.FieldSetting({ name: 'ShortText' }); - expect(inst.Icon).to.be.eq(undefined); - }); - it('returns the name', () => { - expect(fieldSetting.Name).to.be.eq('ShortText'); - }); - it('can be changed', () => { - fieldSetting.Name = 'Number'; - expect(fieldSetting.Name).to.be.equal('Number'); - }); - it('should return a FieldSetting object with a name ShortText', () => { - let obj1 = JSON.stringify({ Name: 'ShortText' }); - let obj2 = new FieldSettings.FieldSetting({ - name: 'ShortText', - }); - expect(obj1).to.be.eq(JSON.stringify(obj2)); - }); - it('should return a FieldSetting object with a name ShortText2', () => { - - const fieldSetting = new FieldSettings.FieldSetting({ name: 'ShortText2', displayName: 'AAA' }); - let obj1 = JSON.stringify({ Name: 'ShortText2', DisplayName: 'AAA' }); - expect(JSON.stringify(fieldSetting)).to.be.eq(obj1); - }); - }); - describe('#TextFieldSetting constructor', () => { - const fieldSetting = new FieldSettings.TextFieldSetting({ name: 'ShortText', displayName: 'ShortText', maxLength: 2, minLength: 0 }); - it('should return a TextFieldSetting object', () => { - expect(fieldSetting).to.be.an.instanceof(FieldSettings.TextFieldSetting); - }); - it('should return a TextFieldSetting object with a name ShortText', () => { - expect(fieldSetting.Name).to.be.eq('ShortText'); - }); - it('should return a TextFieldSetting object with the given params', () => { - const obj1 = JSON.stringify(fieldSetting); - const obj2 = JSON.stringify({ Name: 'ShortText', DisplayName: 'ShortText', MinLength: 0, MaxLength: 2 }) - expect(obj1).to.be.eq(obj2); - }); - it('should return null as the Icon of the TextFieldSetting object', () => { - let inst = new FieldSettings.TextFieldSetting({ name: 'ShortText' }); - expect(inst.Icon).to.be.eq(undefined); - }); - }); - describe('#ShortText', () => { - const shorttext = new FieldSettings.ShortTextFieldSetting({name: 'text'}); - it('should return a ShortText fieldSetting object', () => { - expect(shorttext).to.be.an.instanceof(FieldSettings.ShortTextFieldSetting); - }); - }); - describe('#Number', () => { - const number = new FieldSettings.NumberFieldSetting({name: 'text'}); - it('should return a Number fieldSetting object', () => { - expect(number).to.be.an.instanceof(FieldSettings.NumberFieldSetting); - }); - }); - describe('#Binary', () => { - const binary = new FieldSettings.BinaryFieldSetting({name: 'text'}); - it('should return a Binary fieldSetting object', () => { - expect(binary).to.be.an.instanceof(FieldSettings.BinaryFieldSetting); - }); - }); - describe('#Captcha', () => { - const captcha = new FieldSettings.CaptchaFieldSetting({ 'displayName': 'Captcha' }); - it('should return a Captcha fieldSetting object', () => { - expect(captcha).to.be.an.instanceof(FieldSettings.CaptchaFieldSetting); + it('should return true for ShortText', () => { + expect(FieldSettings.isFieldSettingOfType(fieldSetting, FieldSettings.ShortTextFieldSetting)).to.be.eq(true); }); }); -}); \ No newline at end of file +}); diff --git a/test/HttpProviderTests.ts b/test/HttpProviderTests.ts index 3134bd8..49ef43a 100644 --- a/test/HttpProviderTests.ts +++ b/test/HttpProviderTests.ts @@ -1,10 +1,11 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import { AjaxRequest } from 'rxjs/observable/dom/AjaxObservable'; import { RxAjaxHttpProvider } from '../src/HttpProviders'; -import { AjaxRequest } from '@reactivex/rxjs'; import { MockHttpProvider } from './Mocks/MockHttpProvider'; const expect = Chai.expect; +// tslint:disable:naming-convention @suite('BaseHttpProvider') export class HttpProviderTests { @@ -14,15 +15,15 @@ export class HttpProviderTests { @test public SetGlobalHeaders() { - let p = new MockHttpProvider(); + const p = new MockHttpProvider(); p.SetGlobalHeader(this._testHeaderName, this._testHeaderValue); - let headers = p.ActualHeaders; + const headers = p.ActualHeaders; Chai.expect(headers[this._testHeaderName as any]).to.be.eq(this._testHeaderValue); } @test public UnsetGlobalHeaders() { - let p = new MockHttpProvider(); + const p = new MockHttpProvider(); p.SetGlobalHeader(this._testHeaderName, this._testHeaderValue); let headers = p.ActualHeaders; Chai.expect(headers[this._testHeaderName as any]).to.be.eq(this._testHeaderValue); @@ -34,10 +35,10 @@ export class HttpProviderTests { @test public 'globalHeaders should override options.headers'() { - let p = new MockHttpProvider(); + const p = new MockHttpProvider(); p.SetGlobalHeader(this._testHeaderName, this._testHeaderValue); - let options: AjaxRequest = { + const options: AjaxRequest = { headers: { } }; @@ -48,7 +49,7 @@ export class HttpProviderTests { @test public 'globalHeaders should be overridden by additional headers'() { - let p = new MockHttpProvider(); + const p = new MockHttpProvider(); p.SetGlobalHeader(this._testHeaderName, this._testHeaderValue); p.Ajax(Object, {}, [{name: this._testHeaderName, value: 'modifiedValue'}]).toPromise(); @@ -57,26 +58,46 @@ export class HttpProviderTests { @test public 'RxHttpProvider Ajax should make an XmlHttpRequest call'(done: MochaDone) { - let p = new RxAjaxHttpProvider(); - (global as any).XMLHttpRequest = class { open() { }; send() { this.readyState = 4; this.status = 200; this.response = {}; this.onreadystatechange() }; setRequestHeader() { }; onreadystatechange: () => void; readyState: number; status: number; response: any }; - p.Ajax(Object, {}).subscribe(result => { + const p = new RxAjaxHttpProvider(); + (global as any).XMLHttpRequest = class { + public open() { /* */ } + public send() { + this.readyState = 1; + // Shouldn't resolve for now + this.onreadystatechange(); + + this.readyState = 4; + this.status = 200; + this.response = {}; + this.onreadystatechange(); + } + public setRequestHeader() { /**/ } + public onreadystatechange: () => void; + public readyState: number; + public status: number; + public response: any; }; + p.Ajax(Object, {}).subscribe((result) => { done(); - }, err => done(err)); + }, (err) => done(err)); } @test public 'RxHttpProvider Upload should make an XmlHttpRequest call and parses response if possible'(done: MochaDone) { (global as any).XMLHttpRequest = class { - open() { }; send() { + public open() { /**/ } public send() { setTimeout(() => { - this.readyState = 4; this.status = 200; this.response = '{"success": "true"}'; this.onreadystatechange() + this.readyState = 1; + this.onreadystatechange(); // Should be skipped + this.readyState = 4; + this.status = 200; + this.response = '{"success": "true"}'; + this.onreadystatechange(); }, 10); - }; setRequestHeader() { }; onreadystatechange: () => void; readyState: number; status: number; response: any + } public setRequestHeader() { /**/ } public onreadystatechange: () => void; public readyState: number; public status: number; public response: any; }; - (global as any).File = class { slice(from: number, size: number) { return '' } }; - (global as any).FormData = class { append() { }; }; - let p = new RxAjaxHttpProvider(); - let file = new File(['alma'], 'alma.txt'); + (global as any).FormData = class { public append() { /**/ } }; + const p = new RxAjaxHttpProvider(); + const file = new File(['alma'], 'alma.txt'); p.Upload(Object, file, { url: '', body: { @@ -85,25 +106,24 @@ export class HttpProviderTests { headers: { 'X-Alma': 1 } - }).subscribe(result => { + }).subscribe((result) => { expect((result as any).success).to.be.eq('true'); - done() - }, err => done(err)); + done(); + }, (err) => done(err)); } @test public 'RxHttpProvider Upload should make an XmlHttpRequest call and returns raw response if failed to parse'(done: MochaDone) { (global as any).XMLHttpRequest = class { - open() { }; send() { + public open() { /**/ } public send() { setTimeout(() => { - this.readyState = 4; this.status = 200; this.response = 'a*b*c'; this.onreadystatechange() + this.readyState = 4; this.status = 200; this.response = 'a*b*c'; this.onreadystatechange(); }, 10); - }; setRequestHeader() { }; onreadystatechange: () => void; readyState: number; status: number; response: any + } public setRequestHeader() { /**/ } public onreadystatechange: () => void; public readyState: number; public status: number; public response: any; }; - (global as any).File = class { slice(from: number, size: number) { return '' } }; - (global as any).FormData = class { append() { }; }; - let p = new RxAjaxHttpProvider(); - let file = new File(['alma'], 'alma.txt'); + (global as any).FormData = class { public append() { /**/ } }; + const p = new RxAjaxHttpProvider(); + const file = new File(['alma'], 'alma.txt'); p.Upload(Object, file, { url: '', body: { @@ -112,25 +132,24 @@ export class HttpProviderTests { headers: { 'X-Alma': 1 } - }).subscribe(result => { + }).subscribe((result) => { expect(result).to.be.eq('a*b*c'); - done() - }, err => done(err)); + done(); + }, (err) => done(err)); } @test public 'RxHttpProvider Upload should distribute an Error if the request has an invalid status'(done: MochaDone) { (global as any).XMLHttpRequest = class { - open() { }; send() { + public open() { /**/ } public send() { setTimeout(() => { - this.readyState = 4; this.status = 404; this.response = 'a*b*c'; this.onreadystatechange() + this.readyState = 4; this.status = 404; this.response = 'a*b*c'; this.onreadystatechange(); }, 10); - }; setRequestHeader() { }; onreadystatechange: () => void; readyState: number; status: number; response: any + } public setRequestHeader() { /**/ } public onreadystatechange: () => void; public readyState: number; public status: number; public response: any; }; - (global as any).File = class { slice(from: number, size: number) { return '' } }; - (global as any).FormData = class { append() { }; }; - let p = new RxAjaxHttpProvider(); - let file = new File(['alma'], 'alma.txt'); + (global as any).FormData = class { public append() { /**/ } }; + const p = new RxAjaxHttpProvider(); + const file = new File(['alma'], 'alma.txt'); p.Upload(Object, file, { url: '', body: { @@ -139,9 +158,9 @@ export class HttpProviderTests { headers: { 'X-Alma': 1 } - }).subscribe(result => { - done('This request should be failed') - }, err => done()); + }).subscribe((result) => { + done('This request should be failed'); + }, (err) => done()); } -} \ No newline at end of file +} diff --git a/test/JwtServiceTests.ts b/test/JwtServiceTests.ts index 1689ffc..38bace8 100644 --- a/test/JwtServiceTests.ts +++ b/test/JwtServiceTests.ts @@ -1,10 +1,13 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { JwtService, TokenStore, TokenPersist, LoginResponse, LoginState } from '../src/Authentication'; +import { BehaviorSubject, } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; + +import { JwtService, LoginResponse, LoginState, TokenPersist, TokenStore } from '../src/Authentication'; import { MockHttpProvider } from './Mocks/MockHttpProvider'; -import { Observable, BehaviorSubject } from '@reactivex/rxjs'; import { MockTokenFactory } from './Mocks/MockTokenFactory'; +// tslint:disable:no-string-literal const expect = Chai.expect; @@ -17,41 +20,40 @@ export class JwtServiceTests { private _httpProvider: MockHttpProvider; private _jwtService: JwtService; - before() { + // tslint:disable-next-line:naming-convention + public before() { this._httpProvider = new MockHttpProvider(); this._jwtService = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'session'); } @test public 'Construct with session persistance'() { - let store = this._jwtService['_tokenStore'] as TokenStore; + const store = this._jwtService['_tokenStore'] as TokenStore; expect(store['_tokenPersist']).to.be.eq(TokenPersist.Session); } @test public 'State change should update global header on HttpProvider to access token head & payload'() { - let headers = this._httpProvider.ActualHeaders as any; - let validToken = MockTokenFactory.CreateValid(); + const headers = this._httpProvider.ActualHeaders as any; + const validToken = MockTokenFactory.CreateValid(); expect(headers['X-Access-Data']).to.be.eq(undefined); - (this._jwtService['_tokenStore'] as TokenStore).AccessToken = validToken; // Token.FromHeadAndPayload('a.b'); (this._jwtService['_stateSubject'] as BehaviorSubject).next(LoginState.Authenticated); expect(headers['X-Access-Data']).to.be.eq(validToken.toString()); } - @test public 'Construct with expiration persistance'() { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; expect(store['_tokenPersist']).to.be.eq(TokenPersist.Expiration); } @test public 'checkForUpdate should return an observable'() { - let obs = this._jwtService.CheckForUpdate(); + const obs = this._jwtService.CheckForUpdate(); expect(obs).to.be.instanceof(Observable); } @@ -60,14 +62,14 @@ export class JwtServiceTests { this._httpProvider.AddResponse({ access: 'invalidEncodedValue', refresh: 'invalidEncodedValue' - } as LoginResponse) + } as LoginResponse); - let obs = this._jwtService.Login('usr', 'pass'); - obs.subscribe(t => { + const obs = this._jwtService.Login('usr', 'pass'); + obs.subscribe((t) => { expect(t).to.be.eq(false); expect(this._jwtService.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); - }, err => { + }, (err) => { done(err); }); } @@ -75,130 +77,130 @@ export class JwtServiceTests { @test public 'Error response from Http endpoint response sould be emit False'(done: MochaDone) { this._httpProvider.AddError('Error happened :('); - let obs = this._jwtService.Login('usr', 'pass'); - obs.subscribe(t => { + const obs = this._jwtService.Login('usr', 'pass'); + obs.subscribe((t) => { expect(t).to.be.eq(false); expect(this._jwtService.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); - }, err => { + }, (err) => { done(err); }); } - @test 'CheckForUpdate should resolve with false and state should be Authenticated, if the access token is valid'(done: MochaDone) { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + @test public 'CheckForUpdate should resolve with false and state should be Authenticated, if the access token is valid'(done: MochaDone) { + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); - t.CheckForUpdate().first().subscribe(result => { + t.CheckForUpdate().first().subscribe((result) => { expect(result).to.be.eq(false); - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Authenticated]); + expect(t.CurrentState).to.be.eq(LoginState.Authenticated); done(); - }) + }); } - @test 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token has been expired'(done: MochaDone) { + @test public 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token has been expired'(done: MochaDone) { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', MockTokenFactory.CreateExpired()); - t.CheckForUpdate().first().subscribe(result => { + t.CheckForUpdate().first().subscribe((result) => { expect(result).to.be.eq(false); - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Unauthenticated]); + expect(t.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); - }) + }); } - @test 'CheckForUpdate should resolve with true and state should be Authenticated, if refresh token is valid, but the access token has been expired and the request was valid'(done: MochaDone) { - let refreshToken = MockTokenFactory.CreateValid(); + @test public 'CheckForUpdate should resolve with true and state should be Authenticated, if refresh token is valid, but the access token has been expired and the request was valid'(done: MochaDone) { + const refreshToken = MockTokenFactory.CreateValid(); this._httpProvider.AddResponse({ access: MockTokenFactory.CreateValid().toString(), refresh: refreshToken.toString() - }) - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + }); + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', refreshToken); - t.CheckForUpdate().first().subscribe(result => { + t.CheckForUpdate().first().subscribe((result) => { expect(result).to.be.eq(true); - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Authenticated]); + expect(t.CurrentState).to.be.eq(LoginState.Authenticated); done(); - }) + }); } - @test 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token is valid, but the access token has been expired and the request has failed'(done: MochaDone) { - let refreshToken = MockTokenFactory.CreateValid(); + @test public 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token is valid, but the access token has been expired and the request has failed'(done: MochaDone) { + const refreshToken = MockTokenFactory.CreateValid(); this._httpProvider.AddError(new Error('There was some error during the token refresh request.')); - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', refreshToken); - t.CheckForUpdate().first().subscribe(result => { + t.CheckForUpdate().first().subscribe((result) => { done('This request should be failed, but it succeeded.'); }, (err) => { - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Unauthenticated]); + expect(t.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); }); } - @test 'Login should resolve with true and set state to Authenticated, when request succeeded. '(done: MochaDone) { - let refreshToken = MockTokenFactory.CreateValid(); - this._httpProvider.AddResponse({ + @test public 'Login should resolve with true and set state to Authenticated, when request succeeded. '(done: MochaDone) { + const refreshToken = MockTokenFactory.CreateValid(); + this._httpProvider.AddResponse({ access: MockTokenFactory.CreateValid().toString(), refresh: refreshToken.toString() }); - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - t.Login('user', 'pass').first().subscribe(result => { - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Authenticated]); + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + t.Login('user', 'pass').first().subscribe((result) => { + expect(t.CurrentState).to.be.eq(LoginState.Authenticated); done(); }, (err) => { done(err); }); } - @test 'Login should resolve with false and set state to Unauthenticated, when request failed. '(done: MochaDone) { + @test public 'Login should resolve with false and set state to Unauthenticated, when request failed. '(done: MochaDone) { this._httpProvider.AddError(new Error('There was some error during the token refresh request.')); - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - t.Login('user', 'pass').first().subscribe(result => { - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Unauthenticated]); + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + t.Login('user', 'pass').first().subscribe((result) => { + expect(t.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); }, (err) => { done(err); }); } - @test 'Logout should invalidate both Access and Refresh tokens' (done: MochaDone) { + @test public 'Logout should invalidate both Access and Refresh tokens'(done: MochaDone) { this._httpProvider.AddResponse({success: true}); - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); store.SetToken('refresh', MockTokenFactory.CreateValid()); - t.CheckForUpdate().subscribe(result => { - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Authenticated]); - t.Logout().subscribe(result => { - expect(LoginState[t.CurrentState]).to.be.eq(LoginState[LoginState.Unauthenticated]); + t.CheckForUpdate().subscribe((result) => { + expect(t.CurrentState).to.be.eq(LoginState.Authenticated); + t.Logout().subscribe((newState) => { + expect(t.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); }); }); } - @test 'CurrentUser should return BuiltIn\\Visitor by default' () { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + @test public 'CurrentUser should return BuiltIn\\Visitor by default'() { + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); expect(t.CurrentUser).to.be.eq('BuiltIn\\Visitor'); } - @test 'CurrentUser should return user from payload when access token is set and valid' () { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + @test public 'CurrentUser should return user from payload when access token is set and valid'() { + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); expect(t.CurrentUser).to.be.eq('BuiltIn\\Mock'); } - @test 'CurrentUser should return user from payload when refresh token is set and valid' () { - let t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); - let store = t['_tokenStore'] as TokenStore; + @test public 'CurrentUser should return user from payload when refresh token is set and valid'() { + const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const store = t['_tokenStore'] as TokenStore; store.SetToken('refresh', MockTokenFactory.CreateValid()); expect(t.CurrentUser).to.be.eq('BuiltIn\\Mock'); } -} \ No newline at end of file +} diff --git a/test/Mocks/MockAuthService.ts b/test/Mocks/MockAuthService.ts index 6f77dd5..460868b 100644 --- a/test/Mocks/MockAuthService.ts +++ b/test/Mocks/MockAuthService.ts @@ -1,11 +1,14 @@ /** * @module Mocks */ /** */ +import { BehaviorSubject, } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; +import { ReplaySubject } from 'rxjs/ReplaySubject'; + import { IAuthenticationService, LoginState } from '../../src/Authentication'; -import { Observable, BehaviorSubject, ReplaySubject } from '@reactivex/rxjs'; export class MockAuthService implements IAuthenticationService { - CurrentUser: string = 'BuiltIn\\Visitor'; + public CurrentUser: string = 'BuiltIn\\Visitor'; public StateSubject: BehaviorSubject; constructor() { @@ -13,21 +16,21 @@ export class MockAuthService implements IAuthenticationService { } public ValidUserName: string; - public ValidPassword: string + public ValidPassword: string; - public get State(): Observable{ + public get State(): Observable { return this.StateSubject.asObservable(); } - public get CurrentState(): LoginState{ + public get CurrentState(): LoginState { return this.StateSubject.value; } - CheckForUpdate() { + public CheckForUpdate() { return Observable.from([false]); } - Login(username: string, password: string): Observable { - let subject = new ReplaySubject(); - if (username === this.ValidUserName && password === this.ValidPassword){ + public Login(username: string, password: string): Observable { + const subject = new ReplaySubject(); + if (username === this.ValidUserName && password === this.ValidPassword) { subject.next(true); } else { subject.next(false); @@ -35,8 +38,8 @@ export class MockAuthService implements IAuthenticationService { return subject.asObservable(); } - Logout(): Observable { + public Logout(): Observable { return Observable.from([true]); } -} \ No newline at end of file +} diff --git a/test/Mocks/MockHttpProvider.ts b/test/Mocks/MockHttpProvider.ts index 4ac3654..d7e3678 100644 --- a/test/Mocks/MockHttpProvider.ts +++ b/test/Mocks/MockHttpProvider.ts @@ -2,7 +2,10 @@ * @module Mocks */ /** */ -import { Observable, ReplaySubject, AjaxRequest } from '@reactivex/rxjs'; +import { Observable } from 'rxjs/Observable'; +import { AjaxRequest } from 'rxjs/observable/dom/AjaxObservable'; +import { ReplaySubject } from 'rxjs/ReplaySubject'; + import { BaseHttpProvider } from '../../src/HttpProviders'; /** @@ -10,8 +13,8 @@ import { BaseHttpProvider } from '../../src/HttpProviders'; */ export class MockResponse { - IsError: boolean; - Response: any; + public IsError: boolean; + public Response: any; } export class MockHttpProvider extends BaseHttpProvider { @@ -19,11 +22,9 @@ export class MockHttpProvider extends BaseHttpProvider { private _responseQueue: MockResponse[] = []; public readonly RequestLog: { Options: AjaxRequest, Response: MockResponse }[] = []; - private _lastUploadOptions?: AjaxRequest & { url: string; }; protected uploadInner(returnType: new (...args: any[]) => T, File: File, options?: (AjaxRequest & { url: string; })): Observable { - let subject = new ReplaySubject(); + const subject = new ReplaySubject(); this.UseTimeout ? setTimeout(() => this.runMocks(subject, options as AjaxRequest)) : this.runMocks(subject, options as AjaxRequest); - this._lastUploadOptions = options; return subject.asObservable(); } @@ -44,7 +45,7 @@ export class MockHttpProvider extends BaseHttpProvider { public UseTimeout: boolean = true; private runMocks(subject: ReplaySubject, options: AjaxRequest) { - let response = this._responseQueue[0]; + const response = this._responseQueue[0]; if (response) { this.RequestLog.push({ Response: response, Options: options }); if (response.IsError) { @@ -61,8 +62,8 @@ export class MockHttpProvider extends BaseHttpProvider { } protected ajaxInner(tReturnType: new (...args: any[]) => T, options: AjaxRequest): Observable { - let subject = new ReplaySubject(); + const subject = new ReplaySubject(); this.UseTimeout ? setTimeout(() => this.runMocks(subject, options)) : this.runMocks(subject, options); return subject.asObservable(); } -} \ No newline at end of file +} diff --git a/test/Mocks/MockRepository.ts b/test/Mocks/MockRepository.ts index f8a8675..109f5c6 100644 --- a/test/Mocks/MockRepository.ts +++ b/test/Mocks/MockRepository.ts @@ -2,17 +2,17 @@ * @module Mocks */ /** */ -import { BaseRepository } from '../../src/Repository/index'; -import { MockHttpProvider } from './MockHttpProvider'; import { SnConfigModel } from '../../src/Config'; +import { BaseRepository } from '../../src/Repository/index'; import { MockAuthService } from './MockAuthService'; +import { MockHttpProvider } from './MockHttpProvider'; -export class MockRepository extends BaseRepository{ +export class MockRepository extends BaseRepository { constructor(config?: Partial) { - if (!config){ + if (!config) { config = new SnConfigModel(); } super(config, MockHttpProvider, MockAuthService); } -} \ No newline at end of file +} diff --git a/test/Mocks/MockTokenFactory.ts b/test/Mocks/MockTokenFactory.ts index 801997f..dade173 100644 --- a/test/Mocks/MockTokenFactory.ts +++ b/test/Mocks/MockTokenFactory.ts @@ -2,18 +2,18 @@ * @module Mocks */ /** */ -import { Token, ITokenPayload } from '../../src/Authentication'; +import { ITokenPayload, Token } from '../../src/Authentication'; export class MockTokenFactory { - private static getStillValidDate(){ - let date = new Date() + private static getStillValidDate() { + const date = new Date(); date.setTime(date.getTime() + 3000000); return date.getTime() / 1000; } private static createWithDates(expiration: number, notBefore: number): Token { - let header = { }; - let payload = { + const header = {}; + const payload = { aud: '', exp: expiration, iat: 0, @@ -23,21 +23,21 @@ export class MockTokenFactory { sub: '' } as ITokenPayload; - let headerEncoded = Buffer.from(JSON.stringify(header)).toString('base64') + const headerEncoded = Buffer.from(JSON.stringify(header)).toString('base64'); - let payloadEncoded = Buffer.from(JSON.stringify(payload)).toString('base64') + const payloadEncoded = Buffer.from(JSON.stringify(payload)).toString('base64'); return Token.FromHeadAndPayload(`${headerEncoded}.${payloadEncoded}`); } - public static CreateValid(){ + public static CreateValid() { return this.createWithDates(this.getStillValidDate(), 1); } - public static CreateExpired(){ + public static CreateExpired() { return this.createWithDates(1, this.getStillValidDate()); } - public static CreateNotValidYet(){ - return this.createWithDates(this.getStillValidDate(), this.getStillValidDate()) + public static CreateNotValidYet() { + return this.createWithDates(this.getStillValidDate(), this.getStillValidDate()); } -} \ No newline at end of file +} diff --git a/test/ODataApiTests.ts b/test/ODataApiTests.ts index f562135..4ca45c7 100644 --- a/test/ODataApiTests.ts +++ b/test/ODataApiTests.ts @@ -1,11 +1,10 @@ -import { Observable } from '@reactivex/rxjs'; import * as Chai from 'chai'; -import { CustomAction, ODataCollectionResponse, ODataApi } from '../src/ODataApi'; -import { Content } from '../src/Content'; -import { MockRepository } from './Mocks/MockRepository'; +import { Observable } from 'rxjs/Observable'; import { LoginState } from '../src/Authentication'; -import { ContentType } from '../src/ContentTypes'; +import { Content } from '../src/Content'; +import { CustomAction, ODataApi, ODataCollectionResponse } from '../src/ODataApi'; import { MockHttpProvider } from './Mocks/MockHttpProvider'; +import { MockRepository } from './Mocks/MockRepository'; const expect = Chai.expect; @@ -15,7 +14,7 @@ export const ODataApiTests = describe('ODataApi', () => { beforeEach(() => { service = new MockRepository(); - odataApi = service.GetODataApi() + odataApi = service.GetODataApi(); service.Authentication.StateSubject.next(LoginState.Authenticated); }); @@ -23,7 +22,7 @@ export const ODataApiTests = describe('ODataApi', () => { it('request a Content and returns an Observable object', () => { expect(typeof odataApi.Get({ path: '/workspace/project' })).to.be.eq('object'); }); - }) + }); describe('#Fetch()', () => { it('request a collection of Content and returns an Observable object', (done) => { @@ -35,107 +34,106 @@ export const ODataApiTests = describe('ODataApi', () => { { Id: 1 } ] } - } as ODataCollectionResponse) - odataApi.Fetch({ path: '/workspace/project' }).first().subscribe(result => { + } as ODataCollectionResponse); + odataApi.Fetch({ path: '/workspace/project' }).first().subscribe((result) => { expect(result.d.results[0].Id).to.be.eq(1); done(); - }, done) + }, done); }); - }) + }); describe('#Post()', () => { it('requests to post a created a Content and returns an Observable object', () => { - let observable = odataApi.Post('/workspace/project', { Name: 'alma' }, Content); + const observable = odataApi.Post('/workspace/project', { Name: 'alma' }); expect(observable).to.be.instanceof(Observable); }); - }) + }); describe('#Delete()', () => { it('requests to delete a Content and returns an Observable object', () => { expect(typeof odataApi.Delete(1111, false)).to.be.eq('object'); }); - }) + }); describe('#Patch()', () => { it('requests to patch a Content and returns an Observable object', () => { - expect(typeof odataApi.Patch(1111, Content, { DisplayName: 'test' })).to.be.eq('object'); + expect(typeof odataApi.Patch(1111, { DisplayName: 'test' })).to.be.eq('object'); }); - }) + }); describe('#Put()', () => { it('requests to put a Content and returns an Observable object', () => { - expect(typeof odataApi.Put(1111, ContentType, { + expect(typeof odataApi.Put(1111, { DisplayName: 'test', Type: 'testType', Name: 'alma' })).to.be.eq('object'); }); - }) + }); describe('#CreateCustomAction()', () => { it('requests to create a custom action (checkout) by id, sends a request and returns an Observable object', () => { - let action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }) + const action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }); expect(typeof odataApi.CreateCustomAction(action)).to.be.eq('object'); }); it('requests to create a custom action (checkout) by path, sends a request and returns an Observable object', () => { - let action = new CustomAction({ name: 'CheckOut', path: '/workspaces/project', isAction: true }) + const action = new CustomAction({ name: 'CheckOut', path: '/workspaces/project', isAction: true }); expect(typeof odataApi.CreateCustomAction(action)).to.be.eq('object'); }); it('requests to create a custom action (chekcin) by id, sends a request and returns an Observable object', () => { - let action = new CustomAction({ name: 'CheckIn', id: 111, isAction: true, params: ['checkInComments'] }) - expect(typeof odataApi.CreateCustomAction(action, { data: { 'checkInComments': 'comment' } })).to.be.eq('object'); + const action = new CustomAction({ name: 'CheckIn', id: 111, isAction: true, params: ['checkInComments'] }); + expect(typeof odataApi.CreateCustomAction(action, { data: { checkInComments: 'comment' } })).to.be.eq('object'); }); it('requests to create a custom action (chekcin) by id without cache, sends a request and returns an Observable object', () => { - let action = new CustomAction({ name: 'CheckIn', id: 111, isAction: true, noCache: true, params: ['checkInComments'] }) - expect(typeof odataApi.CreateCustomAction(action, { data: { 'checkInComments': 'comment' } })).to.be.eq('object'); + const action = new CustomAction({ name: 'CheckIn', id: 111, isAction: true, noCache: true, params: ['checkInComments'] }); + expect(typeof odataApi.CreateCustomAction(action, { data: { checkInComments: 'comment' } })).to.be.eq('object'); }); it('requests to create a custom function (getpermissions) by id, sends a request and returns an Observable object', () => { - let action = new CustomAction({ name: 'GetPermission', id: 111, isAction: false, params: ['identity'] }); - expect(typeof odataApi.CreateCustomAction(action, { data: { 'identity': '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } })).to.be.eq('object'); + const action = new CustomAction({ name: 'GetPermission', id: 111, isAction: false, params: ['identity'] }); + expect(typeof odataApi.CreateCustomAction(action, { data: { identity: '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } })).to.be.eq('object'); }); it('should trigger a CustomActionFailed event on a Repository when no Id or Path is provided', (done) => { - let action = new CustomAction({ name: 'GetPermission', isAction: false, params: ['identity'] }); - service.Events.OnCustomActionFailed.subscribe(ac => { + const action = new CustomAction({ name: 'GetPermission', isAction: false, params: ['identity'] }); + service.Events.OnCustomActionFailed.subscribe((ac) => { expect(ac.Error.message).to.be.eq('No Id or Path provided.'); done(); - }) - odataApi.CreateCustomAction(action, { data: { 'identity': '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } }); + }); + odataApi.CreateCustomAction(action, { data: { identity: '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } }); }); - it('should trigger a CustomActionFailed event on a Repository when GET request failed', (done) => { - service.HttpProviderRef.AddError({message: ':('}) - let action = new CustomAction({ name: 'GetPermission', id: 111, isAction: false, params: ['identity'] }); - service.Events.OnCustomActionFailed.subscribe(ac => { + service.HttpProviderRef.AddError({message: ':('}); + const action = new CustomAction({ name: 'GetPermission', id: 111, isAction: false, params: ['identity'] }); + service.Events.OnCustomActionFailed.subscribe((ac) => { expect(ac.Error.message).to.be.eq(':('); done(); - }) - odataApi.CreateCustomAction(action, { data: { 'identity': '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } }); + }); + odataApi.CreateCustomAction(action, { data: { identity: '/Root/Sites/Default_Site/workspaces/Project/budapestprojectworkspace/Groups/Members' } }); }); it('should trigger a CustomActionFailed event on a Repository when POST without Data request failed', (done) => { - service.HttpProviderRef.AddError({message: ':('}) - let action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }) - service.Events.OnCustomActionFailed.subscribe(ac => { + service.HttpProviderRef.AddError({message: ':('}); + const action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }); + service.Events.OnCustomActionFailed.subscribe((ac) => { expect(ac.Error.message).to.be.eq(':('); done(); - }) + }); odataApi.CreateCustomAction(action); }); it('should trigger a OnCustomActionExecuted event on a Repository when POST without Data request succeeded', (done) => { - service.HttpProviderRef.AddResponse({message: ':)'}) - let action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }) - service.Events.OnCustomActionExecuted.subscribe(ac => { + service.HttpProviderRef.AddResponse({message: ':)'}); + const action = new CustomAction({ name: 'CheckOut', id: 111, isAction: true }); + service.Events.OnCustomActionExecuted.subscribe((ac) => { expect(ac.Result.message).to.be.eq(':)'); done(); - }) + }); odataApi.CreateCustomAction(action); }); - }) + }); describe('#Upload()', () => { it('requests to upload a Content and returns an Observable object', () => { @@ -146,21 +144,20 @@ export const ODataApiTests = describe('ODataApi', () => { }); it('Should insert a Slash after OData.Svc for custom actions, if missing ', (done) => { - let http = service.HttpProviderRef; + const http = service.HttpProviderRef; service.Authentication.StateSubject.next(LoginState.Authenticated); service.HttpProviderRef.UseTimeout = true; http.AddResponse({ success: true }); odataApi.CreateCustomAction({ path: `localhost/OData.svc('Root')`, name: 'exampleAction' - }).subscribe(resp => { - }); + }).subscribe((resp) => { /**/ }); setTimeout(() => { expect(http.RequestLog[http.RequestLog.length - 1].Options.url).to.contains('OData.svc/('); done(); - }, 10) + }, 10); }); }); -}); \ No newline at end of file +}); diff --git a/test/ODataHelperTests.ts b/test/ODataHelperTests.ts index 7021bcc..07cd13b 100644 --- a/test/ODataHelperTests.ts +++ b/test/ODataHelperTests.ts @@ -1,9 +1,9 @@ -import { ODataHelper } from '../src/SN' import * as Chai from 'chai'; +import { ODataHelper } from '../src/SN'; import { MockRepository } from './Mocks/MockRepository'; const expect = Chai.expect; -describe('ODataHelper', () => { +export const tests = describe('ODataHelper', () => { describe('#IsItemPath()', () => { it('should return true if the given path is a path of an item', () => { const isAnItem = ODataHelper.isItemPath("/workspace('project')"); @@ -17,7 +17,7 @@ describe('ODataHelper', () => { describe('#getContentUrlbyId()', () => { it('should return a proper item path by the given id', () => { const contentUrl = ODataHelper.getContentUrlbyId(1); - expect(contentUrl).to.be.eq('/content(1)') + expect(contentUrl).to.be.eq('/content(1)'); }); }); describe('#getContentURLbyPath()', () => { @@ -31,10 +31,10 @@ describe('ODataHelper', () => { }); it('should return an error message if the given argument is an empty string', () => { expect(() => {ODataHelper.getContentURLbyPath(''); }) - .to.throws() + .to.throws(); }); it('should return a proper item path for Root only', () => { - let path = ODataHelper.getContentURLbyPath('/Root'); + const path = ODataHelper.getContentURLbyPath('/Root'); expect(path).to.be.eq("/('Root')"); }); }); @@ -52,75 +52,74 @@ describe('#buildUrlParamString()', () => { expect(urlParamString).to.be.eq(''); }); it("should return a string with only select Id and Type if there's no selected field", () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultSelect: ['Id', 'Type']}, { metadata: 'no' }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultSelect: ['Id', 'Type']} as any, { metadata: 'no' }); expect(urlParamString).to.be.eq('metadata=no&$select=Id,Type'); }); it('should return a string with the given field and Id and Type as selected', () => { - const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'}, { select: 'DisplayName' }); + const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'} as any, { select: 'DisplayName' }); expect(urlParamString).to.be.eq('$select=Id,Type,DisplayName&metadata=no'); }); it('should return a string with the given fields and Id and Type as selected', () => { - const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'}, { select: ['DisplayName', 'Path'] }); + const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'} as any, { select: ['DisplayName', 'Path'] }); expect(urlParamString).to.be.eq('$select=Id,Type,DisplayName,Path&metadata=no'); }); it('should return a string with the given parameters', () => { - const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'}, { select: ['DisplayName', 'Path'], orderby: 'DisplayName' }); + const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'} as any, { select: ['DisplayName', 'Path'], orderby: 'DisplayName' }); expect(urlParamString).to.be.eq('$select=Id,Type,DisplayName,Path&$orderby=DisplayName&metadata=no'); }); it('should return a string with the given parameters', () => { - const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'}, { select: ['DisplayName', 'Path'], orderby: 'DisplayName', query: "isOf('Article')" }); + const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'} as any, { select: ['DisplayName', 'Path'], orderby: 'DisplayName', query: "isOf('Article')" }); expect(urlParamString).to.be.eq("$select=Id,Type,DisplayName,Path&$orderby=DisplayName&query=isOf('Article')&metadata=no"); }); it('should return a string without select param', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: 'DisplayName' }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: 'DisplayName' }); expect(urlParamString).to.be.eq('$orderby=DisplayName&metadata=no'); }); it('should parse a single orderby expression', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: 'Name' }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: 'Name' }); expect(urlParamString).to.be.eq('$orderby=Name&metadata=no'); }); it('should parse an orderby array with fields expression', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: ['Name', 'DisplayName'] }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: ['Name', 'DisplayName'] }); expect(urlParamString).to.be.eq('$orderby=Name,DisplayName&metadata=no'); }); it('should parse an orderby field expression with order', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: [['Name', 'asc']] }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: [['Name', 'asc']] }); expect(urlParamString).to.be.eq('$orderby=Name asc&metadata=no'); }); it('should parse an orderby array with ordered fields list expression', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: [['Name', 'asc'], ['DisplayName', 'desc']] }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: [['Name', 'asc'], ['DisplayName', 'desc']] }); expect(urlParamString).to.be.eq('$orderby=Name asc,DisplayName desc&metadata=no'); }); it('should parse an orderby array with ordered fields list expression and field names', () => { - const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'}, { orderby: [['Name', 'asc'], 'DisplayName'] }); + const urlParamString = ODataHelper.buildUrlParamString({DefaultMetadata: 'no'} as any, { orderby: [['Name', 'asc'], 'DisplayName'] }); expect(urlParamString).to.be.eq('$orderby=Name asc,DisplayName&metadata=no'); }); it('should return a string without any param', () => { - const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'}); + const urlParamString = ODataHelper.buildUrlParamString({RequiredSelect: ['Id', 'Type'], DefaultMetadata: 'no'} as any); expect(urlParamString).to.be.eq(''); }); }); - describe('#joinPaths', () => { it('should join with slashes', () => { - let joined = ODataHelper.joinPaths('path1', 'path2', 'path3'); + const joined = ODataHelper.joinPaths('path1', 'path2', 'path3'); expect(joined).to.be.eq('path1/path2/path3'); }); it('should remove slashes from the beginning of the segments', () => { - let joined = ODataHelper.joinPaths('/path1', 'path2', '/path3'); + const joined = ODataHelper.joinPaths('/path1', 'path2/', '/path3'); expect(joined).to.be.eq('path1/path2/path3'); }); it('should remove slashes from the end of the segments', () => { - let joined = ODataHelper.joinPaths('path1', 'path2/', 'path3/'); + const joined = ODataHelper.joinPaths('path1', 'path2/', 'path3/'); expect(joined).to.be.eq('path1/path2/path3'); }); }); diff --git a/test/QueryTests.ts b/test/QueryTests.ts index ceb22ef..b2a6e98 100644 --- a/test/QueryTests.ts +++ b/test/QueryTests.ts @@ -1,9 +1,10 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; +import { LoginState } from '../src/Authentication/LoginState'; +import { Task } from '../src/ContentTypes'; import { Query } from '../src/Query'; -import { ContentTypes } from '../src/SN'; +import { ContentInternal, ContentTypes } from '../src/SN'; import { MockRepository } from './Mocks/index'; -import { LoginState } from '../src/Authentication/LoginState'; const expect = Chai.expect; @@ -11,13 +12,13 @@ const expect = Chai.expect; export class QueryTests { @test public 'Can be constructed'() { - const q = new Query(q => q); - expect(q).to.be.instanceof(Query); + const query = new Query((q) => q); + expect(query).to.be.instanceof(Query); } @test public 'Can be from a repository'(done: MochaDone) { - const repo = new MockRepository() + const repo = new MockRepository(); repo.Authentication.StateSubject.next(LoginState.Authenticated); repo.HttpProviderRef.AddResponse({ d: { @@ -29,34 +30,34 @@ export class QueryTests { Path: 'Root/Tasks' }] } - }) - const query = repo.CreateQuery(q => q.TypeIs(ContentTypes.Folder)); - query.Exec() - .subscribe(res => { + }); + const query = repo.CreateQuery((q) => q.TypeIs(ContentTypes.Folder)); + query.Exec() + .subscribe((res) => { expect(res.Count).to.be.eq(1); - expect(res.Result[0]).to.be.instanceof(ContentTypes.Folder); + expect(res.Result[0]).to.be.instanceof(ContentInternal); expect(res.Result[0].Type).to.be.eq('Folder'); done(); }, done); - expect(query.toString()).to.be.eq('TypeIs:Folder'); + expect(query.toString()).to.be.eq('TypeIs:Folder'); } @test public 'Should throw Error when try to run from a Content without Path'() { - const repo = new MockRepository() + const repo = new MockRepository(); const content = repo.HandleLoadedContent({ Id: 3, Type: 'Folder', - } as any) + } as any); - expect(() => content.CreateQuery(q => q.TypeIs(ContentTypes.Folder))).to.throw('No Content path provided for querying'); + expect(() => content.CreateQuery((q) => q.TypeIs(ContentTypes.Folder))).to.throw('No Content path provided for querying'); } @test public 'Can be from a Content'(done: MochaDone) { - const repo = new MockRepository() + const repo = new MockRepository(); repo.Authentication.StateSubject.next(LoginState.Authenticated); repo.HttpProviderRef.AddResponse({ d: { @@ -68,195 +69,192 @@ export class QueryTests { Path: 'Root/Folders' }] } - }) + }); const content = repo.HandleLoadedContent({ Id: 3, Path: 'Root/Content/Folders', - Type: 'Folder' - }) + Type: 'Folder', + Name: 'test' + }); - const query = content.CreateQuery(q => q.TypeIs(ContentTypes.Folder)) - query.Exec().subscribe(res => { + const query = content.CreateQuery((q) => q.TypeIs(ContentTypes.Folder)); + query.Exec().subscribe((res) => { expect(res.Count).to.be.eq(1); - expect(res.Result[0]).to.be.instanceof(ContentTypes.Folder); + expect(res.Result[0]).to.be.instanceof(ContentInternal); expect(res.Result[0].Type).to.be.eq('Folder'); done(); }, done); expect(query.toString()).to.be.eq('TypeIs:Folder'); - } @test public 'Term syntax'() { - const queryInstance = new Query(q => q.Term('test term')); - expect(queryInstance.toString()).to.be.eq('test term') + const queryInstance = new Query((q) => q.Term('test term')); + expect(queryInstance.toString()).to.be.eq('test term'); } @test public 'TypeIs syntax'() { - const queryInstance = new Query(q => q.TypeIs(ContentTypes.Task)); + const queryInstance = new Query((q) => q.TypeIs(ContentTypes.Task)); expect(queryInstance.toString()).to.be.eq('TypeIs:Task'); } @test public 'Type syntax'() { - const queryInstance = new Query(q => q.Type(ContentTypes.Task)); + const queryInstance = new Query((q) => q.Type(ContentTypes.Task)); expect(queryInstance.toString()).to.be.eq('Type:Task'); } @test public 'InFolder with Path'() { - const queryInstance = new Query(q => q.InFolder('a/b/c')); - expect(queryInstance.toString()).to.be.eq('InFolder:"a/b/c"') + const queryInstance = new Query((q) => q.InFolder('a/b/c')); + expect(queryInstance.toString()).to.be.eq('InFolder:"a/b/c"'); } @test public 'InFolder with Content'() { const repo = new MockRepository(); - const content = repo.CreateContent({ Id: 2, Path: 'a/b/c', Name: 'test', Type: 'Task' }, ContentTypes.Task); - const queryInstance = new Query(q => q.InFolder(content)); - expect(queryInstance.toString()).to.be.eq('InFolder:"a/b/c"') + const content = repo.HandleLoadedContent({ Id: 2, Path: 'a/b/c', Name: 'test', Type: 'Task' }); + const queryInstance = new Query((q) => q.InFolder(content)); + expect(queryInstance.toString()).to.be.eq('InFolder:"a/b/c"'); } @test public 'InTree with Path'() { - const queryInstance = new Query(q => q.InTree('a/b/c')); - expect(queryInstance.toString()).to.be.eq('InTree:"a/b/c"') + const queryInstance = new Query((q) => q.InTree('a/b/c')); + expect(queryInstance.toString()).to.be.eq('InTree:"a/b/c"'); } @test public 'InTree with Content'() { const repo = new MockRepository(); - const content = repo.CreateContent({ Id: 2, Path: 'a/b/c', Name: 'test', Type: 'Task' }, ContentTypes.Task); - const queryInstance = new Query(q => q.InTree(content)); - expect(queryInstance.toString()).to.be.eq('InTree:"a/b/c"') + const content = repo.HandleLoadedContent({ Id: 2, Path: 'a/b/c', Name: 'test', Type: 'Task' }); + const queryInstance = new Query((q) => q.InTree(content)); + expect(queryInstance.toString()).to.be.eq('InTree:"a/b/c"'); } @test public 'Equals'() { - const queryInstance = new Query(q => q.Equals('DisplayName', 'test')); - expect(queryInstance.toString()).to.be.eq('DisplayName:\'test\'') + const queryInstance = new Query((q) => q.Equals('DisplayName', 'test')); + expect(queryInstance.toString()).to.be.eq('DisplayName:\'test\''); } @test public 'NotEquals'() { - const queryInstance = new Query(q => q.NotEquals('DisplayName', 'test')); - expect(queryInstance.toString()).to.be.eq('NOT(DisplayName:\'test\')') + const queryInstance = new Query((q) => q.NotEquals('DisplayName', 'test')); + expect(queryInstance.toString()).to.be.eq('NOT(DisplayName:\'test\')'); } @test public 'Between exclusive'() { - const queryInstance = new Query(q => q.Between('Index', 1, 5)); - expect(queryInstance.toString()).to.be.eq('Index:{\'1\' TO \'5\'}') + const queryInstance = new Query((q) => q.Between('Index', 1, 5)); + expect(queryInstance.toString()).to.be.eq('Index:{\'1\' TO \'5\'}'); } @test public 'Between Inclusive'() { - const queryInstance = new Query(q => q.Between('Index', 10, 50, true, true)); - expect(queryInstance.toString()).to.be.eq('Index:[\'10\' TO \'50\']') + const queryInstance = new Query((q) => q.Between('Index', 10, 50, true, true)); + expect(queryInstance.toString()).to.be.eq('Index:[\'10\' TO \'50\']'); } @test public 'GreatherThan Exclusive'() { - const queryInstance = new Query(q => q.GreatherThan('Index', 10)); - expect(queryInstance.toString()).to.be.eq('Index:>\'10\'') + const queryInstance = new Query((q) => q.GreatherThan('Index', 10)); + expect(queryInstance.toString()).to.be.eq('Index:>\'10\''); } - @test public 'GreatherThan Inclusive'() { - const queryInstance = new Query(q => q.GreatherThan('Index', 10, true)); - expect(queryInstance.toString()).to.be.eq('Index:>=\'10\'') + const queryInstance = new Query((q) => q.GreatherThan('Index', 10, true)); + expect(queryInstance.toString()).to.be.eq('Index:>=\'10\''); } @test public 'LessThan Exclusive'() { - const queryInstance = new Query(q => q.LessThan('Index', 10)); - expect(queryInstance.toString()).to.be.eq('Index:<\'10\'') + const queryInstance = new Query((q) => q.LessThan('Index', 10)); + expect(queryInstance.toString()).to.be.eq('Index:<\'10\''); } - @test public 'LessThan Inclusive'() { - const queryInstance = new Query(q => q.LessThan('Index', 10, true)); - expect(queryInstance.toString()).to.be.eq('Index:<=\'10\'') + const queryInstance = new Query((q) => q.LessThan('Index', 10, true)); + expect(queryInstance.toString()).to.be.eq('Index:<=\'10\''); } @test - public 'AND syntax'(){ - const queryInstance = new Query(q => q.Equals('Index', 1).And.Equals('DisplayName', 'Test')); - expect(queryInstance.toString()).to.be.eq("Index:'1' AND DisplayName:'Test'") + public 'AND syntax'() { + const queryInstance = new Query((q) => q.Equals('Index', 1).And.Equals('DisplayName', 'Test')); + expect(queryInstance.toString()).to.be.eq("Index:'1' AND DisplayName:'Test'"); } @test - public 'OR syntax'(){ - const queryInstance = new Query(q => q.Equals('Index', 1).Or.Equals('DisplayName', 'Test')); - expect(queryInstance.toString()).to.be.eq("Index:'1' OR DisplayName:'Test'") + public 'OR syntax'() { + const queryInstance = new Query((q) => q.Equals('Index', 1).Or.Equals('DisplayName', 'Test')); + expect(queryInstance.toString()).to.be.eq("Index:'1' OR DisplayName:'Test'"); } @test public 'inner Query'() { - const queryInstance = new Query(q => q.Equals('DisplayName', 'Test') + const queryInstance = new Query((q) => q.Equals('DisplayName', 'Test') .And - .Query(inner => + .Query((inner) => inner.Equals('Index', 1) ) - ) + ); expect(queryInstance.toString()).to.be.eq("DisplayName:'Test' AND (Index:'1')"); } @test public 'NOT statement'() { - const queryInstance = new Query(q => q.Equals('DisplayName', 'Test') + const queryInstance = new Query((q) => q.Equals('DisplayName', 'Test') .And - .Not(inner => + .Not((inner) => inner.Equals('Index', 1) ) - ) + ); expect(queryInstance.toString()).to.be.eq("DisplayName:'Test' AND NOT(Index:'1')"); } @test public 'OrderBy'() { - const queryInstance = new Query(q => q.Sort('DisplayName')); + const queryInstance = new Query((q) => q.Sort('DisplayName')); expect(queryInstance.toString()).to.be.eq(" .SORT:'DisplayName'"); } @test public 'OrderBy Reverse'() { - const queryInstance = new Query(q => q.Sort('DisplayName', true)); + const queryInstance = new Query((q) => q.Sort('DisplayName', true)); expect(queryInstance.toString()).to.be.eq(" .REVERSESORT:'DisplayName'"); } - @test public 'Top'() { - const queryInstance = new Query(q => q.Top(50)); + const queryInstance = new Query((q) => q.Top(50)); expect(queryInstance.toString()).to.be.eq(' .TOP:50'); } @test public 'Skip'() { - const queryInstance = new Query(q => q.Skip(10)); + const queryInstance = new Query((q) => q.Skip(10)); expect(queryInstance.toString()).to.be.eq(' .SKIP:10'); } @test - public 'Issue Example output'(){ - const query = new Query(q => + public 'Issue Example output'() { + const query = new Query((q) => q.TypeIs(ContentTypes.Task) // adds '+TypeIs:Document' and Typescript type cast .And .Equals('DisplayName', 'Unicorn') // adds +Title:Unicorn (TBD: fuzzy/Proximity) .And .Between('ModificationDate', '2017-01-01T00:00:00', '2017-02-01T00:00:00') .Or - .Query(sub => sub //Grouping + .Query((sub) => sub // Grouping .NotEquals('Approvable', true) .And - .NotEquals('Description', '*alma*') //Contains with wildcards + .NotEquals('Description', '*alma*') // Contains with wildcards ) .Sort('DisplayName') .Top(5) // adds .TOP:5 @@ -264,7 +262,7 @@ export class QueryTests { ); expect(query.toString()).to.be - .eq("TypeIs:Task AND DisplayName:'Unicorn' AND ModificationDate:{'2017-01-01T00\\:00\\:00' TO '2017-02-01T00\\:00\\:00'} OR (NOT(Approvable:'true') AND NOT(Description:'*alma*')) .SORT:'DisplayName' .TOP:5 .SKIP:10") + .eq("TypeIs:Task AND DisplayName:'Unicorn' AND ModificationDate:{'2017-01-01T00\\:00\\:00' TO '2017-02-01T00\\:00\\:00'} OR (NOT(Approvable:'true') AND NOT(Description:'*alma*')) .SORT:'DisplayName' .TOP:5 .SKIP:10"); } -} \ No newline at end of file +} diff --git a/test/RepositoryTests.ts b/test/RepositoryTests.ts index 233cd2b..2f74767 100644 --- a/test/RepositoryTests.ts +++ b/test/RepositoryTests.ts @@ -1,13 +1,15 @@ import * as Chai from 'chai'; import { suite, test } from 'mocha-typescript'; -import { SnConfigModel } from '../src/Config'; -import { MockRepository, MockHttpProvider } from './Mocks'; -import { VersionInfo, SnRepository, UploadResponse, UploadProgressInfo } from '../src/Repository'; -import { Content } from '../src/Content'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; import { LoginState } from '../src/Authentication'; -import { ODataCollectionResponse, ODataApi } from '../src/ODataApi'; -import { User, Task, ContentType, File as SnFile } from '../src/ContentTypes'; -import { Observable, Subject } from '@reactivex/rxjs'; +import { SnConfigModel } from '../src/Config'; +import { Content, ContentInternal } from '../src/Content'; +import { ContentType, File as SnFile, Task, User } from '../src/ContentTypes'; +import { ODataApi, ODataBatchResponse, ODataCollectionResponse } from '../src/ODataApi'; +import { SnRepository, UploadProgressInfo, UploadResponse, VersionInfo } from '../src/Repository'; +import { Schema, SchemaStore } from '../src/Schemas'; +import { MockHttpProvider, MockRepository } from './Mocks'; const expect = Chai.expect; @@ -16,6 +18,7 @@ export class RepositoryTests { private _repo: MockRepository; + // tslint:disable-next-line:naming-convention public before() { this._repo = new MockRepository({ RepositoryUrl: 'https://localhost', @@ -23,48 +26,47 @@ export class RepositoryTests { }); this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - }; + } @test public 'ODataBaseUrl should return a valid URL based on RepositoryUrl and ODataToken'() { expect(this._repo.ODataBaseUrl).to.be.eq('https://localhost/odata.svc'); } - @test 'GetVersionInfo should return a valid Version Info'() { - let vResponse = new VersionInfo(); + @test public 'GetVersionInfo should return a valid Version Info'() { + const vResponse = new VersionInfo(); vResponse.DatabaseAvailable = true; (this._repo.HttpProviderRef as MockHttpProvider).AddResponse(vResponse); - this._repo.GetVersionInfo().first().subscribe(result => { + this._repo.GetVersionInfo().first().subscribe((result) => { expect(result.DatabaseAvailable).to.be.eq(true); - }) + }); } - @test 'GetAllContentTypes should be return a valid content type collection'(done: MochaDone) { + @test public 'GetAllContentTypes should be return a valid content type collection'(done: MochaDone) { - let cResponse = { + const cResponse = { d: { __count: 1, results: [ { Name: 'testContentType', Type: 'ContentType', - options: {}, } ] } } as ODataCollectionResponse; this._repo.HttpProviderRef.AddResponse(cResponse); - this._repo.GetAllContentTypes().first().subscribe(types => { + this._repo.GetAllContentTypes().first().subscribe((types) => { expect(types.length).to.be.eq(1); expect(types[0].Name).to.be.eq('testContentType'); - expect(types[0]).to.be.instanceof(ContentType); + expect(types[0]).to.be.instanceof(ContentInternal); done(); - }, done) + }, done); } - @test 'Load should return a valid Content'(done: MochaDone) { - let cResponse = { + @test public 'Load should return a valid Content'(done: MochaDone) { + const cResponse = { d: { Name: 'testContentType', Type: 'ContentType' @@ -72,136 +74,168 @@ export class RepositoryTests { }; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef.AddResponse(cResponse); - this._repo.Load(1).first().subscribe(response => { + this._repo.Load(1).first().subscribe((response) => { expect(response.Name).to.be.eq('testContentType'); - expect(response).to.be.instanceof(Content); + expect(response).to.be.instanceof(ContentInternal); done(); - }, err => { + }, (err) => { done(err); }); } - @test 'Load should return a valid Content with a valid type, if defined'(done: MochaDone) { - let cResponse = { + @test public 'Load should return a valid Content with a valid type, if defined'(done: MochaDone) { + const cResponse = { d: { Name: 'testContentType', Type: 'User', + LoginName: 'alba' } }; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef.AddResponse(cResponse); - this._repo.Load(1, {}, User).first().subscribe(response => { + this._repo.Load(1).first().subscribe((response) => { + expect(response.LoginName).to.be.eq('alba'); // For type checking expect(response.Name).to.be.eq('testContentType'); - expect(response).to.be.instanceof(User); + expect(response).to.be.instanceof(ContentInternal); done(); - }, err => { + }, (err) => { done(err); }); } - @test 'SnRepository should have a default Config, if not provided'() { - let snRepo = new SnRepository(); + @test public 'SnRepository should have a default Config, if not provided'() { + const snRepo = new SnRepository(); expect(snRepo.Config.RepositoryUrl).to.be.eq(SnConfigModel.DEFAULT_BASE_URL); } - @test 'SnRepository should respect the provided config'() { - let snRepo = new SnRepository(new SnConfigModel({ + @test public 'SnRepository should respect the provided config'() { + const snRepo = new SnRepository(new SnConfigModel({ RepositoryUrl: 'https://demo.sensenet.com' })); expect(snRepo.Config.RepositoryUrl).to.be.eq('https://demo.sensenet.com'); } - @test 'HandleLoadedContent should respect content type from Options'() { - let snRepo = new SnRepository(new SnConfigModel({ + @test public 'HandleLoadedContent should respect content type (with fields) from generic'() { + const snRepo = new SnRepository(new SnConfigModel({ RepositoryUrl: 'https://demo.sensenet.com' })); - const task = snRepo.HandleLoadedContent({ + const task = snRepo.HandleLoadedContent({ Id: 100, Path: 'Root/Test', - Type: 'Task' - }) + Type: 'Task', + Name: 'Task', + DueText: 'testDueText' + }); - const usr = snRepo.HandleLoadedContent({ + const usr = snRepo.HandleLoadedContent({ Id: 200, Path: 'Root/Test', - Name: 'User' - }, User) + Name: 'User', + Type: 'User', + LoginName: 'testLoginName' + }); const content = snRepo.HandleLoadedContent({ Id: 300, Path: 'Root/Test', Name: '' - }) - expect(task).to.be.instanceof(Task); + }); + expect(task).to.be.instanceof(ContentInternal); + expect(task.DueText).to.be.eq('testDueText'); - expect(usr).to.be.instanceof(User); + expect(usr).to.be.instanceof(ContentInternal); + expect(usr.LoginName).to.be.eq('testLoginName'); - expect(content).to.be.instanceof(Content); + expect(content).to.be.instanceof(ContentInternal); } - @test 'Content should return an ODataApi instance'() { - let snRepo = new SnRepository(); + @test public 'Content should return an ODataApi instance'() { + const snRepo = new SnRepository(); expect(snRepo.Content).to.be.instanceOf(ODataApi); } - @test 'Should be able to create content using repository.CreateContent() '() { - let snRepo = new SnRepository(); - let exampleTask = snRepo.CreateContent({}, Task); - expect(exampleTask).to.be.instanceOf(Task); + @test public 'Should be able to create content using repository.CreateContent() '() { + const snRepo = new SnRepository(); + const exampleTask = snRepo.CreateContent({ DueText: 'testDueText' }, Task); + expect(exampleTask).to.be.instanceOf(ContentInternal); + expect(exampleTask.DueText).to.be.eq('testDueText'); } - - @test 'DeleteBatch() should fire a DeleteBatch request'(done: MochaDone) { - this._repo.HttpProviderRef.AddResponse({}) - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.DeleteBatch([testContent]).subscribe(r => { + @test public 'DeleteBatch() should fire a DeleteBatch request'(done: MochaDone) { + this._repo.HttpProviderRef.AddResponse({ d: { __count: 0, errors: [], results: [] } } as ODataBatchResponse); + const testContent = this._repo.HandleLoadedContent({ + Id: 12345, Path: 'Root/Test', Name: 'Task' + }); + this._repo.DeleteBatch([testContent]).subscribe((r) => { expect(this._repo.HttpProviderRef.LastOptions.url).to.contains("https://localhost/odata.svc/('Root')/DeleteBatch"); - expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('[{"paths":[12345]},{"permanently":false}]'); + expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('{"paths":[12345],"permanent":false}'); expect(this._repo.HttpProviderRef.LastOptions.method).to.be.eq('POST'); done(); - }, err => done(err)); + }, (err) => { + done(err); + }); } - - @test 'DeleteBatch() should fire a DeleteBatch request by path'(done: MochaDone) { + @test public 'DeleteBatch() should fire a DeleteBatch request by path'(done: MochaDone) { const testContentWithoutId = this._repo.HandleLoadedContent({ Path: 'Root/Test2' } as any); - this._repo.HttpProviderRef.AddResponse({}) - this._repo.DeleteBatch([testContentWithoutId]).subscribe(res => { + this._repo.HttpProviderRef.AddResponse({ d: { __count: 0, errors: [], results: [] } } as ODataBatchResponse); + this._repo.DeleteBatch([testContentWithoutId]).subscribe((res) => { expect(this._repo.HttpProviderRef.LastOptions.url).to.contains("https://localhost/odata.svc/('Root')/DeleteBatch"); - expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('[{"paths":["Root/Test2"]},{"permanently":false}]'); + expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('{"paths":["Root/Test2"],"permanent":false}'); expect(this._repo.HttpProviderRef.LastOptions.method).to.be.eq('POST'); done(); - }, err => done(err)); + }, (err) => done(err)); } - @test 'DeleteBatch() should trigger ContentDeleted event after success'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentDeleted.subscribe(c => { + @test public 'DeleteBatch() should trigger ContentDeleted event after success'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'Task' }); + this._repo.Events.OnContentDeleted.subscribe((c) => { expect(c.ContentData.Id).to.be.eq(testContent.Id); done(); }); - this._repo.HttpProviderRef.AddResponse({}); + this._repo.HttpProviderRef.AddResponse({ d: { __count: 1, errors: [], results: [{ Id: 12345, Path: 'Root/Test', Name: 'Task' }] } } as ODataBatchResponse); const action = this._repo.DeleteBatch([testContent]); expect(action).to.be.instanceof(Observable); } - @test 'DeleteBatch() should trigger ContentDeleteFailed event after failure'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentDeleteFailed.subscribe(c => { + @test public 'DeleteBatch() should trigger ContentDeleteFailed event on errored operations'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'Task' }); + this._repo.Events.OnContentDeleteFailed.subscribe((c) => { expect(c.Content).to.be.eq(testContent); done(); - }); - this._repo.HttpProviderRef.AddError({ message: ':(' }); + }, (err) => done(err)); + this._repo.HttpProviderRef.AddResponse({ + d: { + __count: 1, + results: [], + errors: [ + { + content: { Id: 12345, Path: 'Root/Test', Name: 'Task' }, + error: { message: '' } + } + ] + } + } as ODataBatchResponse); + const action = this._repo.DeleteBatch([testContent]); + expect(action).to.be.instanceof(Observable); + } + + @test public 'DeleteBatch() should return error on fail'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'Task' }); + this._repo.HttpProviderRef.AddError(Error(':(' )); const action = this._repo.DeleteBatch([testContent]); + action.subscribe(() => { + done('This shouldn\'t be triggered'); + }, (err) => done()); expect(action).to.be.instanceof(Observable); } - @test 'MoveBatch() should fire a MoveBatch request'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.HttpProviderRef.AddResponse({}) - this._repo.MoveBatch([testContent], 'Root/Test2').subscribe(r => { + @test public 'MoveBatch() should fire a MoveBatch request'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'Task' }); + this._repo.HttpProviderRef.AddResponse({d: { __count: 0}} as ODataBatchResponse); + this._repo.MoveBatch([testContent], 'Root/Test2').subscribe((r) => { expect(this._repo.HttpProviderRef.LastOptions.url).to.contains("https://localhost/odata.svc/('Root')/MoveBatch"); expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('[{"paths":["Root/Test"],"targetPath":"Root/Test2"}]'); expect(this._repo.HttpProviderRef.LastOptions.method).to.be.eq('POST'); @@ -209,79 +243,123 @@ export class RepositoryTests { }); } - @test 'MoveBatch() should trigger ContentMoved event after success'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); + @test public 'MoveBatch() should trigger ContentMoved event after success'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); const sourcePath = testContent.Path; - this._repo.Events.OnContentMoved.subscribe(c => { + this._repo.Events.OnContentMoved.subscribe((c) => { expect(c.Content.Id).to.be.eq(testContent.Id); expect(c.From).to.be.eq(sourcePath); - expect(c.To).to.be.eq('Root/Test2') + expect(c.To).to.be.eq('Root/Test2'); done(); }); - this._repo.HttpProviderRef.AddResponse({}); + this._repo.HttpProviderRef.AddResponse({d: { __count: 1, errors: [], results: [{ + Id: 12345, + Path: 'Root/Test2', + Name: 'Task' + }]}} as ODataBatchResponse); const action = this._repo.MoveBatch([testContent], 'Root/Test2'); expect(action).to.be.instanceof(Observable); } - @test 'MoveBatch() should trigger ContentDeleteFailed event after failure'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentMoveFailed.subscribe(c => { + @test public 'MoveBatch() should trigger ContentMoveFailed event after failure'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnContentMoveFailed.subscribe((c) => { expect(c.Content).to.be.eq(testContent); done(); }); + this._repo.HttpProviderRef.AddResponse({d: { __count: 1, results: [], errors: [{ + content: { + Id: 12345, + Path: 'Root/Test2', + Name: 'Task' + }, + error: ':(' + }]}} as ODataBatchResponse); + const action = this._repo.MoveBatch([testContent], 'Root/Test2'); + expect(action).to.be.instanceof(Observable); + } + + @test public 'MoveBatch() should trigger fail on request error'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); this._repo.HttpProviderRef.AddError({ message: ':(' }); const action = this._repo.MoveBatch([testContent], 'Root/Test2'); + action.subscribe(() => { + done('Should fail'); + }, (err) => done()); expect(action).to.be.instanceof(Observable); } - @test 'CopyBatch() should fire a CopyBatch request'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.HttpProviderRef.AddResponse({}); - this._repo.CopyBatch([testContent], 'Root/Test2').subscribe(r => { + @test public 'CopyBatch() should fire a CopyBatch request'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.HttpProviderRef.AddResponse({d: { __count: 0}} as ODataBatchResponse); + this._repo.CopyBatch([testContent], 'Root/Test2').subscribe((r) => { expect(this._repo.HttpProviderRef.LastOptions.url).to.contains("https://localhost/odata.svc/('Root')/CopyBatch"); expect(this._repo.HttpProviderRef.LastOptions.body).to.be.eq('[{"paths":["Root/Test"],"targetPath":"Root/Test2"}]'); expect(this._repo.HttpProviderRef.LastOptions.method).to.be.eq('POST'); done(); - }, err => done(err)); + }, (err) => done(err)); } - @test 'CopyBatch() should trigger ContentCreated event after success'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentCreated.subscribe(c => { + @test public 'CopyBatch() should trigger ContentCreated event after success'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnContentCreated.subscribe((c) => { expect(c.Content.Id).to.be.eq(testContent.Id); done(); }); - this._repo.HttpProviderRef.AddResponse({}); + this._repo.HttpProviderRef.AddResponse({d: { __count: 1, errors: [], results: [{ + Id: 12345, + Path: 'Root/Test2', + Name: 'Task' + }]}} as ODataBatchResponse); const action = this._repo.CopyBatch([testContent], 'Root/Test2'); expect(action).to.be.instanceof(Observable); } - @test 'CopyBatch() should trigger ContentCreateFailed event after failure'(done: MochaDone) { - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentCreateFailed.subscribe(c => { - expect(c.Content).to.be.eq(testContent); + @test public 'CopyBatch() should trigger ContentCreateFailed event after failure'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnContentCreateFailed.subscribe((c) => { + expect(c.Content.Id).to.be.eq(testContent.Id); done(); }); - this._repo.HttpProviderRef.AddError({ message: ':(' }); + this._repo.HttpProviderRef.AddResponse({d: { __count: 1, results: [], errors: [{ + content: { + Id: 12345, + Path: 'Root/Test2', + Name: 'Task' + }, + error: ':(' + }]}} as ODataBatchResponse); const action = this._repo.CopyBatch([testContent], 'Root/Test2'); expect(action).to.be.instanceof(Observable); } - @test 'UploadResponse can be constructed'() { + @test public 'CopyBatch() should fail on request error'(done: MochaDone) { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.HttpProviderRef.AddError({message: ':('}); + const action = this._repo.CopyBatch([testContent], 'Root/Test2'); + action.subscribe((a) => { + done('Should fail'); + }, (err) => { + done(); + }); + expect(action).to.be.instanceof(Observable); + } + + @test public 'UploadResponse can be constructed'() { const model = new UploadResponse('123', 'chunkToken', true, true); expect(model).to.be.instanceof(UploadResponse); } - @test 'Upload() should trigger UploadProgress event'(done: MochaDone) { + @test public 'Upload() should trigger UploadProgress event'(done: MochaDone) { this._repo.Config.ChunkSize = 1024 * 1024; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnUploadProgress.subscribe(pi => { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnUploadProgress.subscribe((pi) => { expect(pi).to.be.instanceof(Object); done(); - }, err => done(err)); + }, (err) => done(err)); this._repo.HttpProviderRef .AddResponse({ Id: 12356, Path: 'Root/Test/alma' }) .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); @@ -291,22 +369,19 @@ export class RepositoryTests { size: 8 }); - - testContent.UploadFile({ ContentType: Content, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) - .subscribe(progress => { - - }, err => done(err)); + testContent.UploadFile({ ContentType: SnFile, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) + .subscribe((progress) => { /**/ }, (err) => done(err)); } - @test 'Upload() should trigger ContentCreated event'(done: MochaDone) { + @test public 'Upload() should trigger ContentCreated event'(done: MochaDone) { this._repo.Config.ChunkSize = 1024 * 1024; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnContentCreated.subscribe(pi => { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnContentCreated.subscribe((pi) => { expect(pi).to.be.instanceof(Object); done(); - }, err => done(err)); + }, (err) => done(err)); this._repo.HttpProviderRef .AddResponse({ Id: 12356, Path: 'Root/Test/alma' }) .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); @@ -315,41 +390,34 @@ export class RepositoryTests { size: 8 }); - - testContent.UploadFile({ ContentType: Content, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) - .subscribe(progress => { - - }, err => done(err)); + testContent.UploadFile({ ContentType: SnFile, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) + .subscribe((progress) => { /**/ }, (err) => done(err)); } - @test 'Upload() failure should trigger ContentCreateFailed event'(done: MochaDone) { + @test public 'Upload() failure should trigger ContentCreateFailed event'(done: MochaDone) { this._repo.Config.ChunkSize = 1024 * 1024; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef .AddError(Error('e')) .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); - - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); - this._repo.Events.OnUploadProgress.subscribe(pi => { + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); + this._repo.Events.OnUploadProgress.subscribe((pi) => { done('This shouldn\'t be triggered'); }); - this._repo.Events.OnContentCreateFailed.subscribe(failure => { + this._repo.Events.OnContentCreateFailed.subscribe((failure) => { done(); - }) + }); const mockFile = new File(['alma'], 'alma.txt'); Object.assign((mockFile as any), { size: 8 }); - - testContent.UploadFile({ ContentType: Content, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) - .subscribe(progress => { - - }); + testContent.UploadFile({ ContentType: SnFile, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) + .subscribe((progress) => { /**/ }); } - @test 'Upload() chunked content should trigger multiple UploadProgress requests and resolves from Upload observable'(done: MochaDone) { + @test public 'Upload() chunked content should trigger multiple UploadProgress requests and resolves from Upload observable'(done: MochaDone) { this._repo.Config.ChunkSize = 4; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef @@ -357,15 +425,14 @@ export class RepositoryTests { .AddResponse({}) // Mocked chunks .AddResponse({}) .AddResponse({}) - .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }) // Content reload; - + .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); // Content reload; - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); let uploadReqCount = 0; - this._repo.Events.OnUploadProgress.subscribe(pi => { + this._repo.Events.OnUploadProgress.subscribe((pi) => { uploadReqCount = pi.UploadedChunks; - }, err => done(err), () => { + }, (err) => done(err), () => { expect(uploadReqCount).to.be.eq(3); done(); }); @@ -374,9 +441,8 @@ export class RepositoryTests { size: 12 }); - - testContent.UploadFile({ ContentType: Content, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) - .subscribe(progress => { + testContent.UploadFile({ ContentType: SnFile, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) + .subscribe((progress) => { if (progress.Completed) { expect(progress.ChunkCount).to.be.eq(progress.UploadedChunks); expect(uploadReqCount).to.be.eq(progress.ChunkCount); @@ -384,7 +450,7 @@ export class RepositoryTests { } }); } - @test 'Upload() chunked content should trigger multiple UploadProgress requests and resolves from UploadProgress observable'(done: MochaDone) { + @test public 'Upload() chunked content should trigger multiple UploadProgress requests and resolves from UploadProgress observable'(done: MochaDone) { this._repo.Config.ChunkSize = 4; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef @@ -392,35 +458,30 @@ export class RepositoryTests { .AddResponse({}) // Mocked chunks .AddResponse({}) .AddResponse({}) - .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }) // Content reload; - + .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); // Content reload; - const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test' }); + const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); - this._repo.Events.OnUploadProgress.subscribe(progress => { + this._repo.Events.OnUploadProgress.subscribe((progress) => { if (progress.Completed) { expect(progress.ChunkCount).to.be.eq(progress.UploadedChunks); done(); } - }, err => done(err)); + }, (err) => done(err)); const mockFile = new File(['alma'], 'alma.txt'); Object.assign((mockFile as any), { size: 12 }); - - testContent.UploadFile({ ContentType: Content, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) - .subscribe(progress => { - - }, err => done(err)); + testContent.UploadFile({ ContentType: SnFile, File: mockFile as File, PropertyName: 'Binary', Body: {}, Overwrite: true }) + .subscribe((progress) => { /**/ }, (err) => done(err)); } - @test 'UploadTextAsFile should trigger an Upload request'(done: MochaDone) { + @test public 'UploadTextAsFile should trigger an Upload request'(done: MochaDone) { this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - (this._repo as any)['UploadFile'] = (...args: any[]) => { + (this._repo as any).UploadFile = (...args: any[]) => { done(); - } - + }; this._repo.UploadTextAsFile({ Text: 'alma', @@ -432,12 +493,12 @@ export class RepositoryTests { }); } - @test 'UploadFromDropEvent should trigger an Upload request w/o webkitRequestFileSystem'(done: MochaDone) { + @test public 'UploadFromDropEvent should trigger an Upload request w/o webkitRequestFileSystem'(done: MochaDone) { this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - (this._repo as any)['UploadFile'] = (...args: any[]) => { + (this._repo as any).UploadFile = (...args: any[]) => { done(); return Observable.of([true]); - } + }; (global as any).window = {}; const file = new File(['alma.txt'], 'alma'); @@ -458,14 +519,14 @@ export class RepositoryTests { }); } - @test 'UploadFromDropEvent should trigger an Upload request with webkitRequestFileSystem'(done: MochaDone) { + @test public 'UploadFromDropEvent should trigger an Upload request with webkitRequestFileSystem'(done: MochaDone) { this._repo.Authentication.StateSubject.next(LoginState.Authenticated); - (this._repo as any)['UploadFile'] = (...args: any[]) => { + (this._repo as any).UploadFile = (...args: any[]) => { done(); return Observable.of([true]); - } + }; - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; const file = { isFile: true, file: (cb: (f: File) => void) => { cb(new File(['alma.txt'], 'alma')); } @@ -474,7 +535,7 @@ export class RepositoryTests { Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return file } } + { webkitGetAsEntry: () => file } ] } } as any, @@ -486,17 +547,17 @@ export class RepositoryTests { }); } - @test 'UploadFromDropEvent should upload a file and distribute proper status info'(done: MochaDone) { - (this._repo as any)['UploadFile'] = (...args: any[]) => { + @test public 'UploadFromDropEvent should upload a file and distribute proper status info'(done: MochaDone) { + (this._repo as any).UploadFile = (...args: any[]) => { return Observable.of({ ChunkCount: 1, Completed: true, CreatedContent: { Id: 123456 }, UploadedChunks: 1 } as UploadProgressInfo); - } + }; - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; const file = { isFile: true, file: (cb: (f: File) => void) => { cb(new File(['alma.txt'], 'alma')); } @@ -505,7 +566,7 @@ export class RepositoryTests { Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return file } } + { webkitGetAsEntry: () => file } ] } } as any, @@ -513,22 +574,22 @@ export class RepositoryTests { ContentType: SnFile, CreateFolders: false, PropertyName: 'Binary', - Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) - }).then(result => { + Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd', Binary: null as any }, SnFile) + }).then((result) => { done(); - }).catch(err => done(err)); + }).catch((err) => done(err)); } - @test 'UploadFromDropEvent should distribute an error on upload failure'(done: MochaDone) { - (this._repo as any)['UploadFile'] = (...args: any[]) => { + @test public 'UploadFromDropEvent should distribute an error on upload failure'(done: MochaDone) { + (this._repo as any).UploadFile = (...args: any[]) => { const sub = new Subject(); setTimeout(() => { sub.error('erroor'); - }, 10) + }, 10); return sub.asObservable; - } + }; - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; const file = { isFile: true, file: (cb: (f: File) => void) => { cb(new File(['alma.txt'], 'alma')); } @@ -537,7 +598,7 @@ export class RepositoryTests { Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return file } } + { webkitGetAsEntry: () => file } ] } } as any, @@ -545,32 +606,32 @@ export class RepositoryTests { ContentType: SnFile, CreateFolders: false, PropertyName: 'Binary', - Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) - }).then(result => { + Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd', Binary: null as any }, SnFile) + }).then((result) => { done('This shouldn\'t be called on error'); - }).catch(err => done()); + }).catch((err) => done()); } - @test 'UploadFromDropEvent should distribute an error on file read failure'(done: MochaDone) { - (this._repo as any)['UploadFile'] = (...args: any[]) => { + @test public 'UploadFromDropEvent should distribute an error on file read failure'(done: MochaDone) { + (this._repo as any).UploadFile = (...args: any[]) => { return Observable.of({ ChunkCount: 1, Completed: true, CreatedContent: { Id: 123456 }, UploadedChunks: 1 } as UploadProgressInfo); - } + }; - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; const file = { isFile: true, - file: (cb: (f: File) => void, err: (err: any) => void) => { err('File read fails here...') } + file: (cb: (f: File) => void, err: (err: any) => void) => { err('File read fails here...'); } }; this._repo.UploadFromDropEvent({ Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return file } } + { webkitGetAsEntry: () => file } ] } } as any, @@ -578,27 +639,26 @@ export class RepositoryTests { ContentType: SnFile, CreateFolders: false, PropertyName: 'Binary', - Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) - }).then(result => { + Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd', Binary: null as any }, SnFile) + }).then((result) => { done('This shouldn\'t be called on error'); - }).catch(err => done()); + }).catch((err) => done()); } - @test 'UploadFromDropEvent should create Directories'(done: MochaDone) { + @test public 'UploadFromDropEvent should create Directories'(done: MochaDone) { - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; - this._repo.HttpProviderRef.AddResponse({ d: { Id: 123456, Path: 'Root/Folder' } }); + this._repo.HttpProviderRef.AddResponse({ d: { Id: 123456, Path: 'Root/Folder', Name: 'Example' } }); - - (this._repo as any)['UploadFile'] = (...args: any[]) => { + (this._repo as any).UploadFile = (...args: any[]) => { return Observable.of({ ChunkCount: 1, Completed: true, CreatedContent: { Id: 123456 }, UploadedChunks: 1 } as UploadProgressInfo); - } + }; const directory = { isFile: false, isDirectory: true, @@ -610,11 +670,11 @@ export class RepositoryTests { isFile: true, isDirectory: false, name: 'ExampleDirectory', - file: (cb: (f: File) => void) => { cb(new File(['alma.txt'], 'alma')); } + file: (callback: (f: File) => void) => { callback(new File(['alma.txt'], 'alma')); } } - ]) + ]); } - } + }; } }; @@ -622,7 +682,7 @@ export class RepositoryTests { Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return directory } } + { webkitGetAsEntry: () => directory } ] } } as any, @@ -630,24 +690,24 @@ export class RepositoryTests { ContentType: SnFile, CreateFolders: true, PropertyName: 'Binary', - Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) - }).then(result => { + Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd', Binary: null as any }, SnFile) + }).then((result) => { done(); - }).catch(err => done(err)); + }).catch((err) => done(err)); } - @test 'UploadFromDropEvent should fail on error when creating a folder'(done: MochaDone) { + @test public 'UploadFromDropEvent should fail on error when creating a folder'(done: MochaDone) { - (global as any).window = { webkitRequestFileSystem: () => { } }; + (global as any).window = { webkitRequestFileSystem: () => { /**/ } }; this._repo.HttpProviderRef.AddError('neeee'); - (this._repo as any)['UploadFile'] = (...args: any[]) => { + (this._repo as any).UploadFile = (...args: any[]) => { return Observable.of({ ChunkCount: 1, Completed: true, CreatedContent: { Id: 123456 }, UploadedChunks: 1 } as UploadProgressInfo); - } + }; const directory = { isFile: false, isDirectory: true, @@ -657,7 +717,7 @@ export class RepositoryTests { Event: { dataTransfer: { items: [ - { webkitGetAsEntry: () => { return directory } } + { webkitGetAsEntry: () => directory } ] } } as any, @@ -665,27 +725,27 @@ export class RepositoryTests { ContentType: SnFile, CreateFolders: true, PropertyName: 'Binary', - Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) - }).then(result => { + Parent: this._repo.HandleLoadedContent({ Id: 12379846, Path: '/Root/Text', Name: 'asd' }) + }).then((result) => { done('This shouldn\'t be triggered on error'); - }).catch(err => done()); + }).catch((err) => done()); } - @test 'GetCurrentUser() should return an Observable '() { - let repo = new MockRepository(); - expect(repo.GetCurrentUser()).to.be.instanceof(Observable) + @test public 'GetCurrentUser() should return an Observable '() { + const repo = new MockRepository(); + expect(repo.GetCurrentUser()).to.be.instanceof(Observable); } - @test 'GetCurrentUser() should update with Visitor by default '(done: MochaDone) { - let repo = new MockRepository(); - repo.GetCurrentUser().subscribe(u => { + @test public 'GetCurrentUser() should update with Visitor by default '(done: MochaDone) { + const repo = new MockRepository(); + repo.GetCurrentUser().subscribe((u) => { expect(u.Name).to.be.eq('Visitor'); done(); - }, done) + }, done); } - @test 'GetCurrentUser() should update with the new User on change '(done: MochaDone) { - let repo = new MockRepository(); + @test public 'GetCurrentUser() should update with the new User on change '(done: MochaDone) { + const repo = new MockRepository(); repo.HttpProviderRef.AddResponse({ d: { __count: 1, @@ -697,28 +757,27 @@ export class RepositoryTests { Type: 'User', }] } - }) + }); repo.Authentication.CurrentUser = 'BuiltIn\\NewUser'; repo.Authentication.StateSubject.next(LoginState.Pending); repo.Authentication.StateSubject.next(LoginState.Authenticated); - repo.GetCurrentUser().subscribe(u => { + repo.GetCurrentUser().subscribe((u) => { expect(u.Name).to.be.eq('NewUser'); done(); - }, done) + }, done); } - - @test 'GetCurrentUser() should not update if multiple users found on change '(done: MochaDone) { - let repo = new MockRepository(); + @test public 'GetCurrentUser() should not update if multiple users found on change '(done: MochaDone) { + const repo = new MockRepository(); repo.Authentication.StateSubject.next(LoginState.Pending); repo.Authentication.CurrentUser = 'BuiltIn\\NewUser'; - repo.GetCurrentUser().subscribe(u => { + repo.GetCurrentUser().subscribe((u) => { done('Error should be thrown here.'); - }, err => { - expect(err).to.be.eq("Error getting current user: found multiple users with login name 'NewUser' in domain 'BuiltIn'") - done() - }) + }, (err) => { + expect(err).to.be.eq("Error getting current user: found multiple users with login name 'NewUser' in domain 'BuiltIn'"); + done(); + }); repo.HttpProviderRef.AddResponse({ d: { @@ -742,4 +801,18 @@ export class RepositoryTests { repo.Authentication.StateSubject.next(LoginState.Authenticated); } -} \ No newline at end of file + @test public 'SchemaStore should be the generated SchemaStore by default'() { + const localRepo = new MockRepository(); + // tslint:disable-next-line:no-string-literal + expect(localRepo['_schemaStore']).to.be.deep.eq(SchemaStore); + } + + @test public 'Schould be able to update Schemas with SetSchemas()'() { + const localRepo = new MockRepository(); + const newSchemaStore = [{} as Schema]; + localRepo.SetSchemas(newSchemaStore); + // tslint:disable-next-line:no-string-literal + expect(localRepo['_schemaStore']).to.be.deep.eq(newSchemaStore); + } + +} diff --git a/test/RetrierTests.ts b/test/RetrierTests.ts index 561fa46..d2f90ef 100644 --- a/test/RetrierTests.ts +++ b/test/RetrierTests.ts @@ -4,101 +4,100 @@ import { Retrier } from '../src/SN'; const expect = Chai.expect; @suite('Retrier Tests') -export class RetrierTests{ +export class RetrierTests { - @test - public async 'Simple Counter'(){ - let count = 0; - await Retrier.Create(async () => { - count = count + 1; - return count === 3; - }).Run(); - expect(count).to.be.eq(3); - } + @test + public async 'Simple Counter'() { + let count = 0; + await Retrier.Create(async () => { + count = count + 1; + return count === 3; + }).Run(); + expect(count).to.be.eq(3); + } - @test - public async 'onSuccess has been triggered'(){ - let triggered = false; - await Retrier.Create(async () => true) + @test + public async 'onSuccess has been triggered'() { + let triggered = false; + await Retrier.Create(async () => true) .Setup({ OnSuccess: () => { triggered = true; } - }).Run() + }).Run(); - expect(triggered).to.be.eq(true); - } + expect(triggered).to.be.eq(true); + } - @test - public async 'onTimeout has been triggered'(){ - let triggered = false; - await Retrier.Create(async () => false) + @test + public async 'onTimeout has been triggered'() { + let triggered = false; + await Retrier.Create(async () => false) .Setup({ OnFail: () => { triggered = true; }, TimeoutMs: 1 - }).Run() - - expect(triggered).to.be.eq(true); - } + }).Run(); - @test - public async 'onTry has been triggered'(){ - let triggered = false; - await Retrier.Create(async () => true) - .Setup({ - OnTry: () => { - triggered = true; - } - }) - .Run(); - expect(triggered).to.be.eq(true); - } + expect(triggered).to.be.eq(true); + } + @test + public async 'onTry has been triggered'() { + let triggered = false; + await Retrier.Create(async () => true) + .Setup({ + OnTry: () => { + triggered = true; + } + }) + .Run(); + expect(triggered).to.be.eq(true); + } - @test - public async 'exampleTest'(){ - const funcToRetry: () => Promise = async () => { - let hasSucceeded = false; - // ... - // custom logic - // ... - return hasSucceeded; - } - const retrierSuccess = await Retrier.Create(funcToRetry) - .Setup({ - Retries: 3, - RetryIntervalMs: 1, - TimeoutMs: 1000 - }) - .Run(); + @test + public async 'exampleTest'() { + const funcToRetry: () => Promise = async () => { + const hasSucceeded = false; + // ... + // custom logic + // ... + return hasSucceeded; + }; + const retrierSuccess = await Retrier.Create(funcToRetry) + .Setup({ + Retries: 3, + RetryIntervalMs: 1, + TimeoutMs: 1000 + }) + .Run(); - expect(retrierSuccess).to.be.eq(false); - } + expect(retrierSuccess).to.be.eq(false); + } - @test - public async 'Should throw error when started twice'(){ - const retrier = Retrier.Create(async () => {return false; }); + @test + public async 'Should throw error when started twice'() { + const retrier = Retrier.Create(async () => false); - retrier.Run(); + retrier.Run(); - try { - await retrier.Run(); - throw new Error('Should have failed.'); - } catch (error) { - // - } + try { + await retrier.Run(); + throw new Error('Should have failed.'); + } catch (error) { + // } + } - @test - public 'Should throw error when trying to set up after started'(){ - const retrier = Retrier.Create(async () => {return false; }); - retrier.Run(); - expect(() => { - retrier.Setup({ - Retries: 2 - }) - }).to.throw(); - } -} \ No newline at end of file + @test + public 'Should throw error when trying to set up after started'() { + const retrier = Retrier.Create(async () => false); + retrier.Run(); + expect(() => { + retrier.Setup({ + Retries: 2 + }); + }).to.throw(); + } +} diff --git a/test/SchemaTests.ts b/test/SchemaTests.ts deleted file mode 100644 index 331db02..0000000 --- a/test/SchemaTests.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Schema, SchemaStore } from '../src/Schemas'; -import { NumberFieldSetting, BinaryFieldSetting, ChoiceFieldSetting, PasswordFieldSetting } from '../src/FieldSettings'; -import * as Chai from 'chai'; -import { suite, test } from 'mocha-typescript'; -import { Content } from '../src/Content'; - -@suite('Schemas Tests') -export class SchemaTests { - - @test - public 'Should be able to construct'() { - let schema: Schema = new Schema({ - ContentType: Content, - FieldSettings: [ - new NumberFieldSetting({ - name: 'DisplayName' - }), - new BinaryFieldSetting({ - name: 'DisplayName' - }), - new ChoiceFieldSetting({ - name: 'aaa', - options: [] - }), - new PasswordFieldSetting({ - name: 'DisplayName' - }) - ], Icon: '', DisplayName: '', AllowedChildTypes: [], AllowIncrementalNaming: true, AllowIndexing: true, Description: '' - }); - - Chai.expect(schema).to.be.instanceof(Schema); - - } - - @test - public 'SchemaTypes loop'() { - SchemaStore.forEach(schema => { - Chai.expect(schema).to.be.an.instanceof(Schema); - }); - } -} \ No newline at end of file diff --git a/test/SnConfigTests.ts b/test/SnConfigTests.ts index 430cc78..bed4a84 100644 --- a/test/SnConfigTests.ts +++ b/test/SnConfigTests.ts @@ -37,7 +37,6 @@ export class SnConfigTests { expect(add).to.throw(Error); } - @test public 'SnConfigFieldModelStore Should throw an error if you try to add a field without StoreKey'() { const add = () => { SnConfigFieldModelStore.Add({ FieldName: 'Example', Question: 'ExampleQuestion', Behavior: SnConfigBehavior.Default }); }; diff --git a/test/TokenStoreTests.ts b/test/TokenStoreTests.ts index 9871465..0a89f24 100644 --- a/test/TokenStoreTests.ts +++ b/test/TokenStoreTests.ts @@ -1,14 +1,16 @@ import * as Chai from 'chai'; -import { TokenStore, TokenPersist, TokenStoreType, Token } from '../src/Authentication'; +import { Token, TokenPersist, TokenStore, TokenStoreType } from '../src/Authentication'; const expect = Chai.expect; class MockStorage { - private _innerStore: any[] = [] + private _innerStore: any[] = []; + // tslint:disable-next-line:naming-convention public getItem(key: string) { return this._innerStore[key as any]; } + // tslint:disable-next-line:naming-convention public setItem(key: string, value: any) { this._innerStore[key as any] = value; } @@ -16,7 +18,7 @@ class MockStorage { type testParams = [TokenPersist, Partial, Storage, Storage, TokenStoreType]; -let tokenStorageStoreParameters = { +const tokenStorageStoreParameters = { 'InMemory with Expiration': [TokenPersist.Expiration, undefined, undefined, undefined, TokenStoreType.InMemory], 'InMemory with Session': [TokenPersist.Session, undefined, undefined, undefined, TokenStoreType.InMemory], @@ -25,22 +27,21 @@ let tokenStorageStoreParameters = { 'Storage with Expiration': [TokenPersist.Expiration, undefined, new MockStorage(), new MockStorage(), TokenStoreType.LocalStorage], 'Storage with Session': [TokenPersist.Session, undefined, new MockStorage(), new MockStorage(), TokenStoreType.SessionStorage], -} +}; export const TokenStoreTests = describe('TokenStores', () => { - for (let key in tokenStorageStoreParameters) { + for (const key in tokenStorageStoreParameters) { if (tokenStorageStoreParameters.hasOwnProperty(key)) { - let siteName: string = 'https://localhost'; - let tokenTemplate: string = '${siteName}-${tokenName}'; - - let element = (tokenStorageStoreParameters as any)[key] as testParams; - let testToken = Token.FromHeadAndPayload('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzZW5zZW5ldCIsInN1YiI6ImF1dGgiLCJhdWQiOiJjbGllbnQiLCJleHAiOjE0OTMzODM3NTQsImlhdCI6MTQ5MzM4MzY5NCwibmJmIjoxNDkzMzgzNjk0LCJuYW1lIjoiQnVpbHRJblxcQWRtaW4ifQ'); + const siteName: string = 'https://localhost'; + const tokenTemplate: string = '${siteName}-${tokenName}'; + const element = (tokenStorageStoreParameters as any)[key] as testParams; + const testToken = Token.FromHeadAndPayload('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzZW5zZW5ldCIsInN1YiI6ImF1dGgiLCJhdWQiOiJjbGllbnQiLCJleHAiOjE0OTMzODM3NTQsImlhdCI6MTQ5MzM4MzY5NCwibmJmIjoxNDkzMzgzNjk0LCJuYW1lIjoiQnVpbHRJblxcQWRtaW4ifQ'); describe(`TokenStore - ${key}`, () => { - let tokenStore = new TokenStore(siteName, tokenTemplate, element[0], element[1] as any, element[2], element[3]) + const tokenStore = new TokenStore(siteName, tokenTemplate, element[0], element[1] as any, element[2], element[3]); it('should construct the proper store', () => { expect(TokenStoreType[tokenStore.TokenStoreType]).to.be.eq(TokenStoreType[element[4]]); }); @@ -59,7 +60,7 @@ export const TokenStoreTests = describe('TokenStores', () => { tokenStore.RefreshToken = testToken; expect(tokenStore.RefreshToken.toString()).to.be.eq(testToken.toString()); }); - }) + }); } } -}) \ No newline at end of file +}); diff --git a/test/TokenTests.ts b/test/TokenTests.ts index 86eb0e5..41e99c7 100644 --- a/test/TokenTests.ts +++ b/test/TokenTests.ts @@ -10,14 +10,13 @@ export class TokenTests { private _head = 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9'; private _payload = 'eyJpc3MiOiJzZW5zZW5ldCIsInN1YiI6ImF1dGgiLCJhdWQiOiJjbGllbnQiLCJleHAiOjE0OTMyODQ1NDYsImlhdCI6MTQ5MzI4NDQ4NiwibmJmIjoxNDkzMjg0NDg2LCJuYW1lIjoiQnVpbHRJblxcQWRtaW4ifQ'; - private createTestToken(){ + private createTestToken() { return Token.FromHeadAndPayload(`${this._head}.${this._payload}`); } - @test - public 'Construct token from encoded head and payload has valid serialized values'(){ - let t = this.createTestToken(); + public 'Construct token from encoded head and payload has valid serialized values'() { + const t = this.createTestToken(); expect(t.Username).to.be.eq('BuiltIn\\Admin'); @@ -27,15 +26,15 @@ export class TokenTests { } @test - public 'Create empty token'(){ - let t = Token.CreateEmpty(); - expect(t.Username).to.be.eq('') + public 'Create empty token'() { + const t = Token.CreateEmpty(); + expect(t.Username).to.be.eq(''); expect(t.IsValid()).to.be.eq(false); } @test - public 'toString should return the original head and payload'(){ - let t = this.createTestToken(); + public 'toString should return the original head and payload'() { + const t = this.createTestToken(); expect(t.toString()).to.be.eq(`${this._head}.${this._payload}`); } -} \ No newline at end of file +} diff --git a/test/index.ts b/test/index.ts index 81e0372..597bd6c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3,7 +3,6 @@ export * from './BinaryFieldTests'; export * from './JwtServiceTests'; export * from './CollectionTests'; export * from './ContentTests'; -export * from './ContentTypeTests'; export * from './ContentReferenceFieldTests'; export * from './ContentListReferenceFieldTests'; export * from './ContentSerializerTests'; @@ -14,32 +13,20 @@ export * from './ODataApiTests'; export * from './ODataHelperTests'; export * from './RepositoryTests'; export * from './RetrierTests'; -export * from './SchemaTests'; export * from './SnConfigTests'; export * from './TokenTests'; export * from './TokenStoreTests'; + export * from './QueryTests'; -// export { -// HttpProviderTests, -// // BinaryFieldTests, -// JwtServiceTests, -// CollectionTests, -// ContentTests, -// ContentTypeTests, -// ContentReferenceFieldTests, -// ContentListReferenceFieldTests, -// ContentSerializerTests, -// ControlMapperTests, -// FieldSettingsTest, -// ComplexTypesTests, -// ODataApiTests, -// ODataHelperTests, -// RepositoryTests, -// RetrierTests, -// SchemaTests, -// SnConfigTests, -// TokenTests, -// TokenStoreTests, -// Query -// } \ No newline at end of file +// tslint:disable:naming-convention + +(global as any).File = class { + public slice(from: number, size: number) { + return ''; + } + + constructor(fileData: any, public readonly name: string) { + + } +}; diff --git a/tsconfig.json b/tsconfig.json index 8262258..bd8372a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "preserveConstEnums": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, + "skipLibCheck": true, "outDir": "./dist" }, "exclude": [ diff --git a/tslint.json b/tslint.json index 81c2042..bce92c3 100644 --- a/tslint.json +++ b/tslint.json @@ -1,27 +1,28 @@ { "rulesDirectory": ["tslint-consistent-codestyle"], + "extends": "tslint:recommended", "rules": { - "comment-format": [ - true - ], - "indent": [ - true, - "spaces" - ], + "array-type": false, "no-trailing-whitespace": { + "severity": "warn" + }, + "member-ordering": false, + "ordered-imports": true, + "variable-name": false, + "max-line-length": false, + "max-classes-per-file": false, + "no-unused-expression": { "severity": "warn", - "options": [] + "options": [ + "allow-fast-null-checks" + ] }, - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": false, - "no-var-keyword": true, - "no-unused-variable": true, - "no-unused-expression": [true, "allow-fast-null-checks"], - "one-line": [ - true, - "check-open-brace" + "forin": [ + false ], + "no-bitwise": false, + "trailing-comma": false, + "object-literal-sort-keys": false, "only-arrow-functions": [ true ], @@ -29,83 +30,33 @@ true, "allow-null-check" ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], "quotemark": [ true, "single", "avoid-escape" ], - "variable-name": false, - "whitespace": { - "severity": "warn", - "options": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ]}, - "max-file-line-count": [ - false - ], + "class-name": { + "severity": "warn" + }, "naming-convention": { "severity": "warn", "options": [ true, + {"type": "method", "filter": "^toJSON$", "format": null}, + {"type": "method", "modifiers": "private", "leadingUnderscore": "forbid", "format":"camelCase"}, {"type": "method", "modifiers": "protected", "leadingUnderscore": "forbid", "format":"camelCase"}, {"type": "method", "modifiers": "public", "leadingUnderscore": "forbid", "format":"PascalCase"}, - {"type": "method", "modifiers": ["public", "static"], "leadingUnderscore": "forbid", "format":"UPPER_CASE"}, + {"type": "method", "modifiers": ["public", "static"], "leadingUnderscore": "forbid", "format":["UPPER_CASE", "PascalCase"]}, {"type": "property", "modifiers": "private", "leadingUnderscore": "require", "format":"camelCase"}, {"type": "property", "modifiers": "protected", "leadingUnderscore": "require", "format":"camelCase"}, {"type": "property", "modifiers": "public", "leadingUnderscore": "forbid", "format": "PascalCase"}, - {"type": "property", "modifiers": ["public", "static"], "leadingUnderscore": "forbid", "format":"UPPER_CASE"} + {"type": "property", "modifiers": ["public", "static"], "leadingUnderscore": "forbid", "format":"UPPER_CASE"}, + + {"type": "enumMember", "format": "PascalCase"} ] } - }, - "jsRules": { - "indent": [ - true, - "spaces" - ], - "no-duplicate-variable": true, - "no-eval": true, - "no-trailing-whitespace": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "semicolon": [ - false - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "variable-name": [ - false, - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] } } \ No newline at end of file From ae3a614f7fdfa3ccbf33692d373df78a5244ba49 Mon Sep 17 00:00:00 2001 From: gallayl Date: Mon, 6 Nov 2017 16:04:19 +0100 Subject: [PATCH 2/4] [KFI]refactor(Collection): Modified Remove() to work with content Ids instead of indexes --- package.json | 2 +- src/Collection.ts | 30 +++++++----------------------- test/CollectionTests.ts | 6 +++--- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index ec16cce..d8bd5e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sn-client-js", - "version": "3.0.0-development.2", + "version": "3.0.0-development.3", "description": "A JavaScript client for Sense/Net ECM that makes it easy to use the REST API of the Content Repository.", "main": "dist/src/SN.js", "files": [ diff --git a/src/Collection.ts b/src/Collection.ts index d5db2db..7ec4351 100644 --- a/src/Collection.ts +++ b/src/Collection.ts @@ -137,25 +137,9 @@ * }); * ``` */ - public Remove(arg: number | number[], permanently: boolean = false): Observable { - if (typeof arg === 'number') { - const content = this._items[arg]; - if (content && content.Id) { - this._items = - this._items.slice(0, arg) - .concat(this._items.slice(arg + 1)); - - return this._odata.Delete(content.Id, permanently ? permanently : false); - } else { - return Observable.of(undefined); - } - } else { - const ids = arg.map((i) => this._items[i].Id); - this._items = - this._items.filter((item, i) => arg.indexOf(i) > -1); - const action = new CustomAction({ name: 'DeleteBatch', path: this.Path, isAction: true, requiredParams: ['paths'] }); - return this._odata.CreateCustomAction(action, { data: [{ paths: ids }, { permanently }] }); - } + public Remove(ids: number[], permanently: boolean = false): Observable { + const contents = this._items.filter((item) => item.Id && ids.indexOf(item.Id) > -1); + return this._repository.DeleteBatch(contents, permanently); } /** * Method to fetch Content from the Content Repository. @@ -179,7 +163,7 @@ * }); * ``` */ - public Read(path: string, options?: IODataParams): Observable { + public Read(path: string, options?: IODataParams): Observable< any > { this.Path = path; const children = this._odata.Fetch({ params: options, @@ -225,7 +209,7 @@ * }); * ``` */ - public Move(arg: number | number[], targetPath: string): Observable { + public Move(arg: number | number[], targetPath: string): Observable < any > { if (typeof arg === 'number') { this._items = this._items.slice(0, arg) @@ -275,7 +259,7 @@ * }); * ``` */ - public Copy(arg: number | number[], targetPath: string): Observable { + public Copy(arg: number | number[], targetPath: string): Observable < any > { if (typeof arg === 'number') { const action = new CustomAction({ name: 'Copy', id: arg, isAction: true, requiredParams: ['targetPath'] }); return this._odata.CreateCustomAction(action, { data: [{ targetPath }] }); @@ -300,7 +284,7 @@ * }); * ``` */ - public AllowedChildTypes(options?: any): Observable { + public AllowedChildTypes(options?: any): Observable < any > { const o: any = {}; if (options) { o.params = options; diff --git a/test/CollectionTests.ts b/test/CollectionTests.ts index 8ee212f..82832e9 100644 --- a/test/CollectionTests.ts +++ b/test/CollectionTests.ts @@ -77,15 +77,15 @@ export const CollectionTests = describe('Collection', () => { }); describe('#Remove()', () => { it('should return an observable', () => { - expect(collection.Remove(1, true)).to.be.instanceof(Observable); + expect(collection.Remove([1], true)).to.be.instanceof(Observable); }); it('should return an observable', () => { - expect(collection.Remove(1)).to.be.instanceof(Observable); + expect(collection.Remove([1])).to.be.instanceof(Observable); }); it('should return an observable', () => { - expect(collection.Remove(999)).to.be.instanceof(Observable); + expect(collection.Remove([999])).to.be.instanceof(Observable); }); it('should return an observable', () => { From 2ed0e11c968e9495ac128b81a751d6eddf1cf51c Mon Sep 17 00:00:00 2001 From: Lajos Date: Fri, 24 Nov 2017 10:41:08 +0100 Subject: [PATCH 3/4] Feature/oauth google (#75) * [KFI]refactor(JwtService): Refactored JWT Service constructing for Oauth part 1. * [KFI]refactor(BaseRepository): improved batch actions in order to work with an Id/Path list too * [KFI]chore(package): bumped version * [KFI]docs(JwtService): Cleanup and improved docs * [KFI]refactor(Repository): Made the Authentication instance non-readonly (SetupGoogleAuth dependency * [KFI]chore(package): bumped dev version * [KFI]feat(Authentication): added OauthProviders, IOauthProvider * [KFI]refactor(Content): Merged TypeGuards into Types module * [KFI]feat(MockAuthService): Implemented Set/Get OauthProviders * [KFI]test(JwtService): Added tests for setting/getting OAuth providers * [KFI]chore(BaseRepository): removed some unneccesary fallbacks * [KFI]test(ContentInternal): fixed case when trying to patch a content w/o Id * [KFI]chore(Repository): fixed error message on error loading the current user * [KFI]chore(BaseRepository): removed unneccessary filter form batch actions * [KFI]test(Repository): fixed user loading error assertion * [KFI]docs(Authentication): added docs for OauthProvider updates * [KFI]docs(updated docs, removed deprecated Upload action): * [KFI]docs(updated ODataApi docs, removed old Upload action): * [KFI]docs(Schemas): fixed docs modules * [KFI]chore(package): bumped version * [KFI]docs(Repository): improved typedoc * [KFI]fix(Query): fixed result type assertion * [KFI]feat(Upload): added additional OData options * [KFI]fix(Repository): fixed content reload on chunked upload * [KFI]fix(Content): fixed bad isOperationInProgress state when creating new content --- package.json | 2 +- src/Authentication/IAuthenticationService.ts | 6 +- src/Authentication/IOauthProvider.ts | 18 ++ src/Authentication/JwtService.ts | 81 +++-- src/Authentication/index.ts | 1 + src/Content/ContentInternal.ts | 308 ++++++------------- src/Content/IContent.ts | 4 + src/Content/ISavedContent.ts | 16 + src/Content/TypeGuards.ts | 40 --- src/Content/Types.ts | 52 ++++ src/Content/index.ts | 44 ++- src/ODataApi/ODataApi.ts | 10 - src/ODataApi/ODataBatchResponse.ts | 7 + src/Query/Query.ts | 4 +- src/Repository/BaseRepository.ts | 93 +++--- src/Repository/UploadModels.ts | 5 + src/Schema/SchemaLoader.ts | 4 + src/Schema/index.ts | 4 + test/ContentTests.ts | 19 +- test/JwtServiceTests.ts | 94 ++++-- test/Mocks/MockAuthService.ts | 24 +- test/ODataApiTests.ts | 6 - test/RepositoryTests.ts | 8 +- 23 files changed, 464 insertions(+), 386 deletions(-) create mode 100644 src/Authentication/IOauthProvider.ts delete mode 100644 src/Content/TypeGuards.ts diff --git a/package.json b/package.json index d8bd5e9..78a98a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sn-client-js", - "version": "3.0.0-development.3", + "version": "3.0.0-development.7", "description": "A JavaScript client for Sense/Net ECM that makes it easy to use the REST API of the Content Repository.", "main": "dist/src/SN.js", "files": [ diff --git a/src/Authentication/IAuthenticationService.ts b/src/Authentication/IAuthenticationService.ts index eec98de..4e3e6d4 100644 --- a/src/Authentication/IAuthenticationService.ts +++ b/src/Authentication/IAuthenticationService.ts @@ -3,7 +3,7 @@ */ /** */ import { Observable } from 'rxjs/Observable'; -import { LoginState } from './'; +import { IOauthProvider, LoginResponse, LoginState } from './'; /** * Interface that describes how injectable Authentication Services should work @@ -42,4 +42,8 @@ export interface IAuthenticationService { CurrentUser: string; + HandleAuthenticationResponse(response: LoginResponse): boolean; + SetOauthProvider(provider: T); + GetOauthProvider(providerType: {new(...args): T}): T; + } diff --git a/src/Authentication/IOauthProvider.ts b/src/Authentication/IOauthProvider.ts new file mode 100644 index 0000000..aa26e33 --- /dev/null +++ b/src/Authentication/IOauthProvider.ts @@ -0,0 +1,18 @@ +/** + * @module Authentication + */ /** */ + +/** + * Interface that represents a basic structure for an additional OAuth Provider + */ +export interface IOauthProvider { + /** + * Method that retrieves the token info + */ + GetToken(): Promise; + + /** + * Method that handles the user login. Should be responsible for updating the Authentication State as well. + */ + Login(token: string): Promise; +} diff --git a/src/Authentication/JwtService.ts b/src/Authentication/JwtService.ts index 8cd3af7..c5a7185 100644 --- a/src/Authentication/JwtService.ts +++ b/src/Authentication/JwtService.ts @@ -5,10 +5,10 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; -import { BaseHttpProvider } from '../HttpProviders/BaseHttpProvider'; +import { BaseRepository } from '../Repository/BaseRepository'; import { ODataHelper } from '../SN'; -import { IAuthenticationService, LoginResponse, LoginState, RefreshResponse, Token, - TokenPersist, TokenStore } from './'; +import { IAuthenticationService, IOauthProvider, LoginResponse, LoginState, RefreshResponse, + Token, TokenPersist, TokenStore } from './'; /** * This service class manages the JWT authentication, the session and the current login state. @@ -17,6 +17,36 @@ export class JwtService implements IAuthenticationService { private readonly _visitorName: string = 'BuiltIn\\Visitor'; + private _oauthProviders: Map<{new(...args): IOauthProvider}, IOauthProvider> = new Map(); + + /** + * Sets a specified OAuth provider + * @param {IOauthProvider} provider The provider instance to be set + * @throws if a provider with the specified type has already been set + */ + public SetOauthProvider(provider: T) { + const providerCtor = provider.constructor as {new(...args)}; + if (this._oauthProviders.has(providerCtor)) { + throw Error(`Provider for '${providerCtor.name}' already set`); + } + this._oauthProviders.set(providerCtor, provider); + } + + /** + * Gets the specified OAuth provider instance + * @param {} providerType The provider type to be retrieved + * @throws if the provider hasn't been registered + */ + public GetOauthProvider(providerType: {new(...args): T}): T { + if (!this._oauthProviders.has(providerType)) { + throw Error(`OAuth provider not found for '${providerType.name}'`); + } + return this._oauthProviders.get(providerType) as T; + } + + /** + * Returns the current user's name as a string. In case of unauthenticated users, it will return 'BuiltIn\Visitor' + */ public get CurrentUser(): string { if (this._tokenStore.AccessToken.IsValid() || this._tokenStore.RefreshToken.IsValid()) { return this._tokenStore.AccessToken.Username || this._tokenStore.RefreshToken.Username; @@ -24,7 +54,7 @@ export class JwtService implements IAuthenticationService { return this._visitorName; } /** - * This subject indicates the current state of the service + * This observable indicates the current state of the service * @default LoginState.Pending */ public get State(): Observable { @@ -39,13 +69,16 @@ export class JwtService implements IAuthenticationService { return this._stateSubject.getValue(); } - private readonly _stateSubject: BehaviorSubject = new BehaviorSubject(LoginState.Pending); + /** + * The private subject for tracking the login state + */ + protected readonly _stateSubject: BehaviorSubject = new BehaviorSubject(LoginState.Pending); /** * The store for JWT tokens */ private _tokenStore: TokenStore = - new TokenStore(this._repositoryUrl, this._tokenTemplate, (this.Persist === 'session') ? TokenPersist.Session : TokenPersist.Expiration); + new TokenStore(this._repository.Config.RepositoryUrl, this._repository.Config.JwtTokenKeyTemplate, (this._repository.Config.JwtTokenPersist === 'session') ? TokenPersist.Session : TokenPersist.Expiration); /** * Executed before each Ajax call. If the access token has been expired, but the refresh token is still valid, it triggers the token refreshing call @@ -69,9 +102,9 @@ export class JwtService implements IAuthenticationService { * @returns {Observable} An observable that will be completed with true on a succesfull refresh */ private execTokenRefresh() { - const refresh = this._httpProviderRef.Ajax(RefreshResponse, { + const refresh = this._repository.HttpProviderRef.Ajax(RefreshResponse, { method: 'POST', - url: ODataHelper.joinPaths(this._repositoryUrl, 'sn-token/refresh'), + url: ODataHelper.joinPaths(this._repository.Config.RepositoryUrl, 'sn-token/refresh'), headers: { 'X-Refresh-Data': this._tokenStore.RefreshToken.toString(), 'X-Authentication-Type': 'Token', @@ -89,30 +122,24 @@ export class JwtService implements IAuthenticationService { } /** - * @param {BaseHttpProvider} httpProviderRef The Http Provider to use (e.g. login / logout / session renew requests) - * @param {string} repositoryUrl The URL for the repository - * @param {string} tokenTemplate The template to use when generating token keys in session/local storage or in a cookie. ${siteName} and ${tokenName} will be replaced. - * @param {'session' | 'expiration'} persist Sets up if the tokens should be persisted per session (browser close) or per token expiration (based on the token) + * @param {BaseRepository} _repository the Repository reference for the Authentication. The service will read its configuration and use its HttpProvider * @constructs JwtService */ - constructor(private readonly _httpProviderRef: BaseHttpProvider, - private readonly _repositoryUrl: string, - private readonly _tokenTemplate: string, - public readonly Persist: 'session' | 'expiration') { + constructor(protected readonly _repository: BaseRepository) { this._stateSubject = new BehaviorSubject(LoginState.Pending); this.State.subscribe((s) => { if (this._tokenStore.AccessToken.IsValid()) { - this._httpProviderRef.SetGlobalHeader('X-Access-Data', this._tokenStore.AccessToken.toString()); + this._repository.HttpProviderRef.SetGlobalHeader('X-Access-Data', this._tokenStore.AccessToken.toString()); } else { - this._httpProviderRef.UnsetGlobalHeader('X-Access-Data'); + this._repository.HttpProviderRef.UnsetGlobalHeader('X-Access-Data'); } }); this.CheckForUpdate(); } - private handleAuthenticationResponse(response: LoginResponse): boolean { + public HandleAuthenticationResponse(response: LoginResponse): boolean { this._tokenStore.AccessToken = Token.FromHeadAndPayload(response.access); this._tokenStore.RefreshToken = Token.FromHeadAndPayload(response.refresh); if (this._tokenStore.AccessToken.IsValid()) { @@ -149,16 +176,16 @@ export class JwtService implements IAuthenticationService { this._stateSubject.next(LoginState.Pending); const authToken: string = new Buffer(`${username}:${password}`).toString('base64'); - this._httpProviderRef.Ajax(LoginResponse, { + this._repository.HttpProviderRef.Ajax(LoginResponse, { method: 'POST', - url: ODataHelper.joinPaths(this._repositoryUrl, 'sn-token/login'), + url: ODataHelper.joinPaths(this._repository.Config.RepositoryUrl, 'sn-token/login'), headers: { 'X-Authentication-Type': 'Token', 'Authorization': `Basic ${authToken}`, }, }) .subscribe((r) => { - const result = this.handleAuthenticationResponse(r); + const result = this.HandleAuthenticationResponse(r); sub.next(result); }, (err) => { this._stateSubject.next(LoginState.Unauthenticated); @@ -169,19 +196,17 @@ export class JwtService implements IAuthenticationService { } /** - * Logs out the current user, sets the tokens to 'empty' - * ``` - * service.Logout(); - * ``` + * Logs out the current user, sets the tokens to 'empty' and sends a Logout request to invalidate all Http only cookies + * @returns {Observable} An Observable that will be updated with the logout response */ public Logout(): Observable { this._tokenStore.AccessToken = Token.CreateEmpty(); this._tokenStore.RefreshToken = Token.CreateEmpty(); this._stateSubject.next(LoginState.Unauthenticated); - return this._httpProviderRef.Ajax(LoginResponse, { + return this._repository.HttpProviderRef.Ajax(LoginResponse, { method: 'POST', - url: ODataHelper.joinPaths(this._repositoryUrl, 'sn-token/logout'), + url: ODataHelper.joinPaths(this._repository.Config.RepositoryUrl, 'sn-token/logout'), }).map(() => true); } diff --git a/src/Authentication/index.ts b/src/Authentication/index.ts index 6456324..2ca99e0 100644 --- a/src/Authentication/index.ts +++ b/src/Authentication/index.ts @@ -5,6 +5,7 @@ */ /** */ export * from './IAuthenticationService'; +export * from './IOauthProvider'; export * from './ITokenPayload'; export * from './JwtService'; export * from './LoginResponse'; diff --git a/src/Content/ContentInternal.ts b/src/Content/ContentInternal.ts index 4d449ec..c43cc4d 100644 --- a/src/Content/ContentInternal.ts +++ b/src/Content/ContentInternal.ts @@ -1,44 +1,5 @@ /** * @module Content - * @preferred - * - * @description Top level base type of sense NET's Type hierarchy. - * - * Content has the basic properties and functionality that can be reached on all of the inherited types. It's almost the same as an abstract class, it may not be instantiated directly, - * but it has the basic methods implemented that can be called on obejcts with derived types. - * - * Unlike other Content Types it is not autogenerated. - * - * Actions - * ------------------ - * - * Built-in SenseNet Actions on client-side are methods on the Content object. If you want to use them you have to have a Content which can be made with the Create method that can convert your js Object or JSON - * with the proper parameters to a Content with the given type. - * ``` - * var content = Content.Create('Folder', {DisplayName: 'My folder'}); - * ``` - * On a Content object the Actions can be called like this way (check below sections for further information about the Actions and their params): - * ``` - * let contentDelete = content.Delete(false); - * ``` - * This function returns an Observable so that you can subscribe to it in your code. This way you can work with the response easier defining the three functions of a subscribtion and - * using all the super helpful and usable features like filtering, combining, caching, etc. - * ``` - * contentDelete.subscribe({ - * .map(response => response.d) //map and use the reponse's 'd' object as the response - * .subscribe({ - * next: response => console.log(response), //if the request was successful - * error: error => console.error('something wrong occurred: ' + error), //if the request failed - * complete: () => console.log('done'), //if the request is done - * }) - * }); - * ``` - * Using RxJS you are able to merge, zip or combine multiple Observables, this way you can develope various combinations of Actions with custom functionality. - * - * Read more about RxJS [here](http://reactivex.io/rxjs) - * - * And about Reactive Programming [here](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) it could be helpful not only with ajax request but also with event handling or anything else - * related to async data streams. */ /** */ import { Observable } from 'rxjs/Observable'; @@ -55,9 +16,11 @@ import { ContentTypes, Enums, FieldSettings, ODataHelper, Schemas, Security } from '../SN'; import { IContent } from './IContent'; import { ISavedContent } from './ISavedContent'; - import { isSavedContent } from './TypeGuards'; - import { Content, SavedContent } from './Types'; + import { Content, isSavedContent, SavedContent } from './Types'; + /** + * Internal class representation of a Content instance. + */ export class ContentInternal { private get _odata(): ODataApi { @@ -229,8 +192,8 @@ /** * @constructs Content - * @param {IContentOptions} options An object implementing IContentOptions interface - * @param {IRepository} repository The Repoitory instance + * @param {IContentOptions} options An object with the required content data + * @param {IRepository} repository The Repository instance */ constructor(_options: T, private _repository: BaseRepository, private readonly _contentType: {new(...args): T}) { Object.assign(this, _options); @@ -243,14 +206,11 @@ * @param permanently {boolean} Determines if the Content should be deleted permanently or moved to the Trash. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let delContent = content.Delete(false); - * delContent.subscribe({ - * next: response => { + * content.Delete(false) + * subscribe( response => { * console.log('success'); * }, - * error: error => console.error('something wrong occurred: ' + error), - * complete: () => console.log('done'), - * }); + * error: error => console.error('something wrong occurred: ' + error)); * ``` */ public Delete(permanently?: boolean): Observable { @@ -282,14 +242,10 @@ * @param {string} newName New name of the content. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let rename = content.Rename('New Title'); - * rename.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.Rename('New Title') + * .subscribe(response => { + * console.log(response); + * }, error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ public Rename(newDisplayName: string, newName?: string): Observable> { @@ -315,7 +271,7 @@ if (!this.Id) { const err = new Error('Content Id not present'); this._repository.Events.Trigger.ContentModificationFailed({ - Content: this.tryGetAsSaved(), + Content: this as any, Fields: fields, Error: err }); @@ -375,6 +331,7 @@ if (!resp.Id) { throw Error('Error: No content Id in response!'); } + this._isOperationInProgress = false; this.updateLastSavedFields(resp); this._repository.HandleLoadedContent(this.tryGetAsSaved()); this._isSaved = true; @@ -382,7 +339,7 @@ }).share(); request.subscribe((c) => { - this._repository.Events.Trigger.ContentCreated({ Content: this.tryGetAsSaved() }); + this._repository.Events.Trigger.ContentCreated({ Content: c }); }, (err) => { this._repository.Events.Trigger.ContentCreateFailed({ Content: this as IContent, Error: err }); }); @@ -417,19 +374,24 @@ /** * Saves the content with its given modified fields to the Content Repository. - * @params fields {Object} Object with the fields that have to be modified. - * @params override {boolean=} [false] Determines whether clear the fields that are not given (true) or leave them and modify only the given fields (false). - * @params options {Object=} JSON object with the possible ODATA parameters like select, expand, etc. + * @param {T?} fields Optional - The fields to be saved. If not provided, the changed fields will be saved + * @param {boolean? } override Determines whether clear the fields that are not given (true) or leave them and modify only the given fields (false). * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let save = content.Save({'Index':2}, true); //Set Index field's value to 2 and clear the rest of the fields. - * save.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * //Set Index field's value to 2 and clear the rest of the fields. + * content.Save({'Index':2}, true) + * .subscribe(response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); + * + * // Update the Description field only + * content.Description = 'New description text'; + * content.Save() //Set Index field's value to 2 and clear the rest of the fields. + * .subscribe(response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ public Save(fields?: T, override?: boolean): Observable> { @@ -437,6 +399,7 @@ this._isOperationInProgress = true; const saveObservable = this.saveContentInternal(fields, override).share(); saveObservable.subscribe((success) => { + success._isOperationInProgress = false; this._isOperationInProgress = false; }, (err) => { this._isOperationInProgress = false; @@ -448,7 +411,7 @@ * Reloads every field and reference of the content, based on the specified View from the Schema * @throws if the Content is not saved yet or no Id or Path is provided * @param {'edit' | 'view'} actionName - * @returns {Observable} An observable whitch will be updated with the Content + * @returns {Observable} An observable whitch will be updated with the reloaded Content */ public Reload(actionName?: 'edit' | 'view'): Observable> { if (!this.IsSaved) { @@ -501,17 +464,14 @@ /** * Method that returns actions of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {string} scenario + * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let actions = content.Actions('ListItem'); - * actions.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.Actions('ListItem') + * .subscribe(response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ public Actions(scenario?: string): Observable { @@ -527,35 +487,31 @@ } /** * Method that returns allowed child type list of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options {Object} JSON object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable[]>} Returns an RxJS observable with the content types of the allowed child types * ``` - * let allowedChildTypes = content.GetAllowedChildTypes(); - * allowedChildTypes.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.GetAllowedChildTypes() + * .subscribe({ + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ - public GetAllowedChildTypes(options?: IODataParams): Observable<(ContentType & ISavedContent)[]> { + public GetAllowedChildTypes(options?: IODataParams): Observable[]> { return this.AllowedChildTypes.GetContent(options); } /** * Method that returns effective allowed child type list of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. + * @param {IODataParams} options Object with the possible ODATA parameters like select, expand, etc. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let allowedChildTypes = content.GetEffectiveAllowedChildTypes(); - * allowedChildTypes.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.GetEffectiveAllowedChildTypes() + * .subscribe({ + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ public GetEffectiveAllowedChildTypes(options?: IODataParams): Observable<(ContentType & ISavedContent)[]> { @@ -563,75 +519,67 @@ } /** * Method that returns owner of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options Object with the possible ODATA parameters like select, expand, etc. + * @returns {ObservableSavedContent} an observable that will be updated with the Owner user. * ``` - * let owner = content.GetOwner({select: ['FullName']}); - * owner.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.GetOwner({select: ['FullName']}) + * .subscribe( + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ - public GetOwner(options?: IODataParams): Observable { + public GetOwner(options?: IODataParams): Observable> { return this.Owner.GetContent(options); } /** * Method that returns creator of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options JSON object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable>} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let creator = content.Creator({select: ['FullName']}); - * creator.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.Creator({select: ['FullName']}) + * .subscribe( + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ - public Creator(options?: IODataParams): Observable { + public Creator(options?: IODataParams): Observable> { return this.CreatedBy.GetContent(options); } /** * Method that returns last modifier of a content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options Object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable>} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let modifier = content.Modifier({select: ['FullName']}); - * modifier.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.Modifier({select: ['FullName']}) + * .subscribe( + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ - public Modifier(options?: IODataParams): Observable { + public Modifier(options?: IODataParams): Observable> { return this.ModifiedBy.GetContent(options); } /** * Method that returns the user who checked-out the content. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options Object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable>} Returns an RxJS observable that you can subscribe of in your code. * ``` - * let checkedOutBy = content.CheckedOutBy({select: ['FullName']}); - * checkedOutBy.subscribe({ - * next: response => { - * console.log(response); - * }, - * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value), - * complete: () => console.log('done'), - * }); + * content.CheckedOutBy({select: ['FullName']}) + * .subscribe( + * response => { + * console.log(response); + * }, + * error: error => console.error('something wrong occurred: ' + error.responseJSON.error.message.value)); * ``` */ - public CheckedOutBy(options?: IODataParams): Observable { + public CheckedOutBy(options?: IODataParams): Observable> { return this.CheckedOutTo.GetContent(options); } /** @@ -640,8 +588,8 @@ * Calls the method [FetchContent]{@link ODataApi.FetchContent} with the content id and the given OData options. * If you leave the options undefined only the Id and the Type fields will be in the response. These two fields are always the part of the reponse whether they're added or not to the options * as selectable. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param { IODataParams } options Object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` * let children = content.Children({select: ['DisplayName']}); * children.subscribe({ @@ -671,8 +619,8 @@ * Calls the method [GetContent]{@link ODataApi.GetContent} with the content id and the given OData options. * If you leave the options undefined only the Id and the Type fields will be in the response. These two fields are always the part of the reponse whether they're added or not to the options * as selectable. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. + * @param {IODataParams} options {Object} JSON object with the possible ODATA parameters like select, expand, etc. + * @returns {Observable[]>} Returns an RxJS observable that you can subscribe of in your code. * ``` * let versions = content.GetVersions(); * versions.subscribe({ @@ -693,7 +641,7 @@ * Calls the method [GetContent]{@link ODataApi.GetContent} with the content id and the given OData options. * If you leave the options undefined only the Id and the Type fields will be in the response. These two fields are always the part of the reponse whether they're added or not to the options * as selectable. - * @params options {Object} JSON object with the possible ODATA parameters like select, expand, etc. + * @param {IODataParams} options Object with the possible ODATA parameters like select, expand, etc. * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. * ``` * let currentWorkspace = content.GetWorkspace(); @@ -1009,20 +957,15 @@ /** * Creates a Content object by the given type and options Object that hold the field values. - * @param type {string} The Content will be a copy of the given type. - * @param options {SenseNet.IContentOptions} Optional list of fields and values. + * @param {T} options Object for initial fields and values + * @param {new(...args: any[]): T} newContent The Content Type definition + * @param {BaseRepository} repository the Repository instance * @returns {SenseNet.Content} * ```ts * var content = SenseNet.Content.Create({ DisplayName: 'My folder' }, ContentTypes.Folder); // content is an instance of the ContentTypes.Folder with the DisplayName 'My folder' * ``` */ - // public static Create(opt: O, newContent: { new(...args: any[]): TContent }, - // repository: BaseRepository): TContent { - // let constructed = new newContent(opt, repository); - // return constructed; - // } - - public static Create(options: T, newContent: {new(...args: any[]): T}, repository: BaseRepository): Content & T { + public static Create(options: T, newContent: {new(...args: any[]): T}, repository: BaseRepository): Content { const created = new ContentInternal(options, repository, newContent) as Content; if (newContent) { created.Type = newContent.name; @@ -1608,53 +1551,6 @@ { data: { contentIds } }); } - /** - * Uploads a stream or text to a content binary field (e.g. a file). - * @params ContentType {string=} Specific content type name for the uploaded content. If not provided, the system will try to determine it from the current environment: the upload content types configured in the - * web.config and the allowed content types in the particular folder. In most cases, this will be File. - * @params FileName {string} Name of the uploaded file. - * @params Overwrite {bool=True} Whether the upload action should overwrite a content if it already exist with the same name. If false, a new file will be created with a similar name containing an - * incremental number (e.g. sample(2).docx). - * @params UseChunk {bool=False} Determines whether the system should start a chunk upload process instead of saving the file in one round. Usually this is determined by the size of the file. - * It's optional, used in the first request - * @params PropertyName {string=Binary} Appoints the binary field of the content where the data should be saved. - * @params ChunkToken {string} The response of first request returns this token. It must be posted in all of the subsequent requests without modification. It is used for executing the chunk upload operation. - * It's mandatory, except in the first request - * @params {FileText} In case you do not have the file as a real file in the file system but a text in the browser, you can provide the raw text in this parameter. - * @returns {Observable} Returns an RxJS observable that you can subscribe of in your code. - */ - public Upload(contentType: string = 'File', fileName: string, overwrite: boolean = true, useChunk: boolean = false, propertyName: string = 'Binary', fileText?: string) { - // tslint:disable-next-line:no-console - console.warn(`This method is deprecated, please use 'UploadFile' or 'UploadText' instead `); - if (!this.Path) { - throw Error('No Path provided!'); - } - - const data: any = { - ContentType: contentType, - FileName: fileName, - Overwrite: overwrite, - UseChunk: useChunk, - propertyName, - }; - - if (typeof fileText !== 'undefined') { - data.FileText = fileText; - } - const uploadCreation = this._odata.Upload(this.Path, data, true); - uploadCreation.subscribe({ - next: (response) => { - return this._odata.Upload(this.Path as string, { - ContentType: contentType, - FileName: fileName, - Overwrite: overwrite, - ChunkToken: response - }, false); - } - }); - return uploadCreation; - } - /** * Uploads a File into a level below the specified Content * @param {UploadFileOptions} uploadOptions The options to the Upload request diff --git a/src/Content/IContent.ts b/src/Content/IContent.ts index 9c022a3..a38c03f 100644 --- a/src/Content/IContent.ts +++ b/src/Content/IContent.ts @@ -1,3 +1,7 @@ +/** + * @module Content + */ /** */ + export interface IContent { Type?: string; /** diff --git a/src/Content/ISavedContent.ts b/src/Content/ISavedContent.ts index ae5a127..c3c28ca 100644 --- a/src/Content/ISavedContent.ts +++ b/src/Content/ISavedContent.ts @@ -1,7 +1,23 @@ +/** + * @module Content + */ /** */ + import { IContent } from './IContent'; +/** + * Interface that represents a saved Content + */ export interface ISavedContent extends IContent { + /** + * Unique identifier + */ Id: number; + /** + * Full Content path + */ Path: string; + /** + * Content Name + */ Name: string; } diff --git a/src/Content/TypeGuards.ts b/src/Content/TypeGuards.ts deleted file mode 100644 index fb22344..0000000 --- a/src/Content/TypeGuards.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { DeferredObject } from '../ComplexTypes'; -import { ContentInternal } from './ContentInternal'; -import { IContent } from './IContent'; -import { SavedContent } from './Types'; - -/** - * Typeguard that determines if the specified Object is a DeferredObject - * @param fieldObject The object that needs to be checked - */ -export const isDeferred = (fieldObject: any): fieldObject is DeferredObject => { - return fieldObject && fieldObject.__deferred && fieldObject.__deferred.uri && fieldObject.__deferred.uri.length > 0 || false; -}; - -/** - * Typeguard that determines if the specified Object is an IContentOptions instance - * @param object The object that needs to be checked - */ -export const isIContent = (object: any): object is IContent => { - return object && object.Id && object.Path && object.Type && object.Type.length > 0 || false; -}; - -/** - * Typeguard that determines if the specified Object is a Content instance - * @param object The object that needs to be checked - */ -export const isContent = (object: any): object is ContentInternal => { - return object instanceof ContentInternal; -}; - -/** - * Typeguard that determines if the specified Object is an IContentOptions array - * @param {any[]} objectList The object that needs to be checked - */ -export const isIContentList = (objectList: any[]): objectList is IContent[] => { - return objectList && objectList.length !== undefined && objectList.find((o) => !isIContent(o)) === undefined || false; -}; - -export const isSavedContent = (c: ContentInternal): c is SavedContent => { - return c && isContent(c) && c.Id && c.Path && c.Path.length && c.Name && c.Name.length > 0 || false; -}; diff --git a/src/Content/Types.ts b/src/Content/Types.ts index 85d151c..867f4f9 100644 --- a/src/Content/Types.ts +++ b/src/Content/Types.ts @@ -1,7 +1,59 @@ +/** + * @module Content + */ /** */ + +import { DeferredObject } from '../ComplexTypes'; import { IContent } from './IContent'; import { ContentInternal } from './index'; import { ISavedContent } from './ISavedContent'; +/** + * Generic type alias for a Content representation. It's possible that a content of this type has not been saved yet to the Repository. + */ export type Content = ContentInternal & T; +/** + * Generic type alias for a saved Content representation. Saved contents *have* an Id, Path and a Name property + */ + export type SavedContent = ContentInternal & T & ISavedContent; + +/** + * Typeguard that determines if the specified Object is a DeferredObject + * @param fieldObject The object that needs to be checked + */ +export const isDeferred = (fieldObject: any): fieldObject is DeferredObject => { + return fieldObject && fieldObject.__deferred && fieldObject.__deferred.uri && fieldObject.__deferred.uri.length > 0 || false; +}; + +/** + * Typeguard that determines if the specified Object is an IContentOptions instance + * @param object The object that needs to be checked + */ +export const isIContent = (object: any): object is IContent => { + return object && object.Id && object.Path && object.Type && object.Type.length > 0 || false; +}; + +/** + * Typeguard that determines if the specified Object is a Content instance + * @param object The object that needs to be checked + */ +export const isContent = (object: any): object is ContentInternal => { + return object instanceof ContentInternal; +}; + +/** + * Typeguard that determines if the specified Object is an IContentOptions array + * @param {any[]} objectList The object that needs to be checked + */ +export const isIContentList = (objectList: any[]): objectList is IContent[] => { + return objectList && objectList.length !== undefined && objectList.find((o) => !isIContent(o)) === undefined || false; +}; + +/** + * Typeguard that determines if the specified Content instance is saved. + * @param { ContentInternal } c The content that needs to be checked + */ +export const isSavedContent = (c: ContentInternal): c is SavedContent => { + return c && isContent(c) && c.Id && c.Path && c.Path.length && c.Name && c.Name.length > 0 || false; +}; diff --git a/src/Content/index.ts b/src/Content/index.ts index 04db39a..d9ff8d3 100644 --- a/src/Content/index.ts +++ b/src/Content/index.ts @@ -1,5 +1,47 @@ +/** + * @module Content + * @preferred + * + * @description Top level base type of sense NET's Type hierarchy. + * + * Content has the basic properties and functionality that can be reached on all of the inherited types. It's almost the same as an abstract class, it may not be instantiated directly, + * but it has the basic methods implemented that can be called on obejcts with derived types. + * + * Unlike other Content Types it is not autogenerated. + * + * Actions + * ------------------ + * + * Built-in SenseNet Actions on client-side are methods on the Content object. If you want to use them you have to have a Content which can be made with the Create method that can convert your js Object or JSON + * with the proper parameters to a Content with the given type. + * ``` + * var content = Content.Create('Folder', {DisplayName: 'My folder'}); + * ``` + * On a Content object the Actions can be called like this way (check below sections for further information about the Actions and their params): + * ``` + * let contentDelete = content.Delete(false); + * ``` + * This function returns an Observable so that you can subscribe to it in your code. This way you can work with the response easier defining the three functions of a subscribtion and + * using all the super helpful and usable features like filtering, combining, caching, etc. + * ``` + * contentDelete.subscribe({ + * .map(response => response.d) //map and use the reponse's 'd' object as the response + * .subscribe({ + * next: response => console.log(response), //if the request was successful + * error: error => console.error('something wrong occurred: ' + error), //if the request failed + * complete: () => console.log('done'), //if the request is done + * }) + * }); + * ``` + * Using RxJS you are able to merge, zip or combine multiple Observables, this way you can develope various combinations of Actions with custom functionality. + * + * Read more about RxJS [here](http://reactivex.io/rxjs) + * + * And about Reactive Programming [here](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) it could be helpful not only with ajax request but also with event handling or anything else + * related to async data streams. + */ /** */ + export * from './IContent'; export * from './ISavedContent'; export * from './ContentInternal'; export * from './Types'; -export * from './TypeGuards'; diff --git a/src/ODataApi/ODataApi.ts b/src/ODataApi/ODataApi.ts index 5129799..2a44415 100644 --- a/src/ODataApi/ODataApi.ts +++ b/src/ODataApi/ODataApi.ts @@ -253,14 +253,4 @@ export class ODataApi { } } } - - public Upload = (path: string, data: any, creation: boolean): Observable => { - let url = `${ODataHelper.getContentURLbyPath(path)}/Upload`; - if (creation) { - url = `${url}?create=1`; - } else { - url = url; - } - return this._repository.Ajax(url, 'POST', Object, data); - } } diff --git a/src/ODataApi/ODataBatchResponse.ts b/src/ODataApi/ODataBatchResponse.ts index 70f07be..fecfcf0 100644 --- a/src/ODataApi/ODataBatchResponse.ts +++ b/src/ODataApi/ODataBatchResponse.ts @@ -1,6 +1,13 @@ +/** + * @module ODataApi + */ /** */ + import { ISavedContent } from '../Content'; // tslint:disable:naming-convention +/** + * Represents a Batch Operation response from Batch Copy/Move/Delete action + */ export class ODataBatchResponse { public d: { diff --git a/src/Query/Query.ts b/src/Query/Query.ts index 968d672..e42ec89 100644 --- a/src/Query/Query.ts +++ b/src/Query/Query.ts @@ -35,7 +35,7 @@ export class Query { return this._segments.map((s) => s.toString()).join(''); } - constructor(build: (first: QueryExpression) => void) { + constructor(build: (first: QueryExpression) => QuerySegment) { const firstExpression = new QueryExpression(this); build(firstExpression); } @@ -66,7 +66,7 @@ export class Query { * Represents a finialized Query instance that has a Repository, path and OData Parameters set up */ export class FinializedQuery extends Query { - constructor(build: (first: QueryExpression) => void, + constructor(build: (first: QueryExpression) => QuerySegment, private readonly _repository: BaseRepository, private readonly _path: string, private readonly _odataParams: IODataParams = {}) { diff --git a/src/Repository/BaseRepository.ts b/src/Repository/BaseRepository.ts index f8bde34..aa1a6c6 100644 --- a/src/Repository/BaseRepository.ts +++ b/src/Repository/BaseRepository.ts @@ -89,12 +89,12 @@ export class BaseRepository((uploadOptions.ContentType || ContentInternal) as { new(...args: any[]): Content }, uploadOptions.File, { + this.HttpProviderRef.Upload((uploadOptions.ContentType) as { new(...args: any[]): Content }, uploadOptions.File, { url: uploadPath, body: uploadOptions.Body, }) .subscribe((created) => { - this.HandleLoadedContent(created as SavedContent).Reload().subscribe((c) => { + this.Load((created as SavedContent).Id, uploadOptions.OdataOptions).subscribe((c) => { this.Events.Trigger.ContentCreated({ Content: c }); @@ -158,22 +158,22 @@ export class BaseRepository { - return this.Load(resp.ContentId) - .map((content) => { - const chunkCount = Math.ceil(uploadOptions.File.size / this.Config.ChunkSize); - // tslint:disable-next-line:no-string-literal - content['_isOperationInProgress'] = false; - const progressInfo = { - Completed: true, - ChunkCount: chunkCount, - UploadedChunks: chunkCount, - CreatedContent: content - } as UploadProgressInfo; - this.Events.Trigger.UploadProgress(progressInfo); - return progressInfo; - }); - }); + return this.Load(resp.ContentId, uploadOptions.OdataOptions) + .map((content) => { + const chunkCount = Math.ceil(uploadOptions.File.size / this.Config.ChunkSize); + // tslint:disable-next-line:no-string-literal + content['_isOperationInProgress'] = false; + const progressInfo = { + Completed: true, + ChunkCount: chunkCount, + UploadedChunks: chunkCount, + CreatedContent: content + } as UploadProgressInfo; + this.Events.Trigger.UploadProgress(progressInfo); + return progressInfo; + }); }); + }); } return uploadSubject.asObservable(); @@ -207,8 +207,8 @@ export class BaseRepository({ Id: contentId, Path: 'asd', - Name: options.File.name || 'File', - Type: options.ContentType.name || 'File' + Name: options.File.name, + Type: options.ContentType.name } as T & ISavedContent); // tslint:disable-next-line:no-string-literal content['_isOperationInProgress'] = true; @@ -326,7 +326,7 @@ export class BaseRepository} * ```ts - * var content = SenseNet.Content.HandleLoadedContent('Folder', { DisplayName: 'My folder' }); // content is an instance of the Folder with the DisplayName 'My folder' + * var content = SenseNet.Content.HandleLoadedContent({ Id: 123456, Path: 'Root/Example', DisplayName: 'My folder' }, ContentTypes.Folder); // content is an instance of the Folder with the DisplayName 'My folder' * ``` */ - public HandleLoadedContent(opt: T & ISavedContent, contentType?: { new(...args: any[]): T }): SavedContent { + public HandleLoadedContent(contentData: T & ISavedContent, contentType?: { new(...args: any[]): T }): SavedContent { let instance: Content; - const realContentType = (contentType || (opt.Type && (ContentTypes as any)[opt.Type]) || Folder) as { new(...args: any[]): T }; + const realContentType = (contentType || (contentData.Type && (ContentTypes as any)[contentData.Type]) || Folder) as { new(...args: any[]): T }; - if (opt.Id) { - const cached = this._loadedContentReferenceCache.get(opt.Id); + if (contentData.Id) { + const cached = this._loadedContentReferenceCache.get(contentData.Id); if (cached) { instance = cached as Content; // tslint:disable-next-line:no-string-literal - instance['updateLastSavedFields'](opt); + instance['updateLastSavedFields'](contentData); } else { - instance = ContentInternal.Create(opt, realContentType, this); - this._loadedContentReferenceCache.set(opt.Id, instance as SavedContent); + instance = ContentInternal.Create(contentData, realContentType, this); + this._loadedContentReferenceCache.set(contentData.Id, instance as SavedContent); } } else { - instance = ContentInternal.Create(opt, realContentType, this); + instance = ContentInternal.Create(contentData, realContentType, this); } // tslint:disable-next-line:no-string-literal instance['_isSaved'] = true; @@ -473,10 +473,13 @@ export class BaseRepository Content} contentType The type of the Content instance + * @returns {Content} the created, unsaved content instance */ public CreateContent: (options: TContentType, contentType: { new(...args: any[]): TContentType }) => Content = - (options: TContentType, contentType: { new(...args: any[]): TContentType }) => +(options: TContentType, contentType: { new(...args: any[]): TContentType }) => ContentInternal.Create(options, contentType, this) /** @@ -538,11 +541,11 @@ export class BaseRepository({ name: 'DeleteBatch', path: rootContent.Path, @@ -550,7 +553,7 @@ export class BaseRepository c.Id || c.Path).filter((c) => c !== undefined), + paths: contentList.map((c) => c.Id || c.Path || c), permanent } @@ -580,11 +583,11 @@ export class BaseRepository{ * console.log('Contents moved.') * }) - * @param {Content[]} contentList An array of content to move + * @param {(SavedContent | number | string)[]} contentList An array of content to move. Can be a content (with path) or a Path * @param {string} targetPath The target Path * @param {Content} rootContent The context node, the PortalRoot by default */ - public MoveBatch(contentList: (SavedContent<{}>)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { + public MoveBatch(contentList: (SavedContent | string)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { const action = this._odataApi.CreateCustomAction>({ name: 'MoveBatch', path: rootContent.Path, @@ -593,7 +596,7 @@ export class BaseRepository c.Path).filter((c) => c !== undefined), + paths: contentList.map((c) => c.Path || c), targetPath }, @@ -632,11 +635,11 @@ export class BaseRepository{ * console.log('Contents copied.') * }) - * @param {Content[]} contentList An array of content to copy + * @param {(SavedContent | number | string)[]} contentList An array of content to copy. Can be a content (with path) or a Path * @param {string} targetPath The target Path * @param {Content} rootContent The context node, the PortalRoot by default */ - public CopyBatch(contentList: (SavedContent)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { + public CopyBatch(contentList: (SavedContent | string)[], targetPath: string, rootContent: Content = this._staticContent.PortalRoot) { const action = this._odataApi.CreateCustomAction({ name: 'CopyBatch', path: rootContent.Path, @@ -645,7 +648,7 @@ export class BaseRepository c.Path).filter((c) => c !== undefined), + paths: contentList.map((c) => c.Path || c), targetPath }, @@ -703,7 +706,7 @@ export class BaseRepository = T & { Parent: SavedContent }; @@ -24,6 +25,10 @@ export class UploadOptions { * Additional options to post */ public Body?: any; + /** + * + */ + public OdataOptions?: IODataParams; } export class UploadFileOptions extends UploadOptions { diff --git a/src/Schema/SchemaLoader.ts b/src/Schema/SchemaLoader.ts index 4f52114..fbfd468 100644 --- a/src/Schema/SchemaLoader.ts +++ b/src/Schema/SchemaLoader.ts @@ -1,3 +1,7 @@ +/** + * @module Schemas + */ /** */ + import { BinaryField } from '../BinaryField'; import { ContentType } from '../ContentTypes'; import { BaseRepository } from '../Repository'; diff --git a/src/Schema/index.ts b/src/Schema/index.ts index 36b089a..17c4e93 100644 --- a/src/Schema/index.ts +++ b/src/Schema/index.ts @@ -1 +1,5 @@ +/** + * @module Schemas + */ /** */ + export * from './SchemaLoader'; diff --git a/test/ContentTests.ts b/test/ContentTests.ts index 5b27373..fda072d 100644 --- a/test/ContentTests.ts +++ b/test/ContentTests.ts @@ -405,6 +405,13 @@ export const contentTests = describe('Content', () => { }, done); }); + it('should throw Error when trying to update specified fields and the Id is not provided', () => { + repo.HttpProviderRef.AddError({ message: 'serverErrorMessage' }); + const c = repo.CreateContent({Path: 'Root/Test', Name: 'asd' }, Task); + + expect(() => {c.Save({ DisplayName: 'new' }); }).to.throw(); + }); + it('should send a PATCH request if fields are specified and override is false', (done) => { repo.HttpProviderRef.AddResponse({ d: { @@ -568,18 +575,6 @@ export const contentTests = describe('Content', () => { }); - describe('#Upload', () => { - it('should return an Observable object', () => { - expect(contentSaved.Upload('Root/Example', 'example.txt', true, true, 'binary', 'example text')).to.be.instanceof(Observable); - }); - - it('should throw an error if no Path specified', () => { - (contentSaved as any).Path = undefined; - expect(() => {contentSaved.Upload('Root/Example', 'example.txt'); }).to.throw('No Path provided!'); - }); - - }); - describe('#Actions()', () => { it('should return an Observable object', () => { expect(contentSaved.Actions()).to.be.instanceof(Observable); diff --git a/test/JwtServiceTests.ts b/test/JwtServiceTests.ts index 38bace8..c274f98 100644 --- a/test/JwtServiceTests.ts +++ b/test/JwtServiceTests.ts @@ -3,27 +3,34 @@ import { suite, test } from 'mocha-typescript'; import { BehaviorSubject, } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Observable'; -import { JwtService, LoginResponse, LoginState, TokenPersist, TokenStore } from '../src/Authentication'; -import { MockHttpProvider } from './Mocks/MockHttpProvider'; +import { IOauthProvider, JwtService, LoginResponse, LoginState, TokenPersist, TokenStore } from '../src/Authentication'; +import { MockRepository } from './Mocks/MockRepository'; import { MockTokenFactory } from './Mocks/MockTokenFactory'; // tslint:disable:no-string-literal const expect = Chai.expect; +export class MockOauthProvider implements IOauthProvider { + public GetToken(): Promise { + throw new Error('Method not implemented.'); + } + public Login(token: string): Promise { + throw new Error('Method not implemented.'); + } + +} + @suite('JwtService') export class JwtServiceTests { - private readonly _hostUrl: string = 'https://localhost'; - private readonly _tokenTemplate: string = '${siteName}-${tokenName}'; - - private _httpProvider: MockHttpProvider; + private _repo: MockRepository; private _jwtService: JwtService; // tslint:disable-next-line:naming-convention public before() { - this._httpProvider = new MockHttpProvider(); - this._jwtService = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'session'); + this._repo = new MockRepository(); + this._jwtService = new JwtService(this._repo); } @test @@ -34,7 +41,7 @@ export class JwtServiceTests { @test public 'State change should update global header on HttpProvider to access token head & payload'() { - const headers = this._httpProvider.ActualHeaders as any; + const headers = this._repo.HttpProviderRef.ActualHeaders as any; const validToken = MockTokenFactory.CreateValid(); expect(headers['X-Access-Data']).to.be.eq(undefined); @@ -46,7 +53,8 @@ export class JwtServiceTests { @test public 'Construct with expiration persistance'() { - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; expect(store['_tokenPersist']).to.be.eq(TokenPersist.Expiration); } @@ -59,7 +67,7 @@ export class JwtServiceTests { @test public 'LoginResponse with invalid token sould be emit False'(done: MochaDone) { - this._httpProvider.AddResponse({ + this._repo.HttpProviderRef.AddResponse({ access: 'invalidEncodedValue', refresh: 'invalidEncodedValue' } as LoginResponse); @@ -76,7 +84,7 @@ export class JwtServiceTests { @test public 'Error response from Http endpoint response sould be emit False'(done: MochaDone) { - this._httpProvider.AddError('Error happened :('); + this._repo.HttpProviderRef.AddError('Error happened :('); const obs = this._jwtService.Login('usr', 'pass'); obs.subscribe((t) => { expect(t).to.be.eq(false); @@ -88,7 +96,8 @@ export class JwtServiceTests { } @test public 'CheckForUpdate should resolve with false and state should be Authenticated, if the access token is valid'(done: MochaDone) { - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); t.CheckForUpdate().first().subscribe((result) => { @@ -99,8 +108,8 @@ export class JwtServiceTests { } @test public 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token has been expired'(done: MochaDone) { - - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', MockTokenFactory.CreateExpired()); @@ -113,11 +122,12 @@ export class JwtServiceTests { @test public 'CheckForUpdate should resolve with true and state should be Authenticated, if refresh token is valid, but the access token has been expired and the request was valid'(done: MochaDone) { const refreshToken = MockTokenFactory.CreateValid(); - this._httpProvider.AddResponse({ + this._repo.Config.JwtTokenPersist = 'expiration'; + this._repo.HttpProviderRef.AddResponse({ access: MockTokenFactory.CreateValid().toString(), refresh: refreshToken.toString() }); - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', refreshToken); @@ -130,8 +140,9 @@ export class JwtServiceTests { @test public 'CheckForUpdate should resolve with false and state should be Unauthenticated, if refresh token is valid, but the access token has been expired and the request has failed'(done: MochaDone) { const refreshToken = MockTokenFactory.CreateValid(); - this._httpProvider.AddError(new Error('There was some error during the token refresh request.')); - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + this._repo.HttpProviderRef.AddError(new Error('There was some error during the token refresh request.')); + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateExpired()); store.SetToken('refresh', refreshToken); @@ -145,11 +156,12 @@ export class JwtServiceTests { @test public 'Login should resolve with true and set state to Authenticated, when request succeeded. '(done: MochaDone) { const refreshToken = MockTokenFactory.CreateValid(); - this._httpProvider.AddResponse({ + this._repo.HttpProviderRef.AddResponse({ access: MockTokenFactory.CreateValid().toString(), refresh: refreshToken.toString() }); - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); t.Login('user', 'pass').first().subscribe((result) => { expect(t.CurrentState).to.be.eq(LoginState.Authenticated); done(); @@ -159,8 +171,9 @@ export class JwtServiceTests { } @test public 'Login should resolve with false and set state to Unauthenticated, when request failed. '(done: MochaDone) { - this._httpProvider.AddError(new Error('There was some error during the token refresh request.')); - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.HttpProviderRef.AddError(new Error('There was some error during the token refresh request.')); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); t.Login('user', 'pass').first().subscribe((result) => { expect(t.CurrentState).to.be.eq(LoginState.Unauthenticated); done(); @@ -170,8 +183,9 @@ export class JwtServiceTests { } @test public 'Logout should invalidate both Access and Refresh tokens'(done: MochaDone) { - this._httpProvider.AddResponse({success: true}); - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.HttpProviderRef.AddResponse({success: true}); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); store.SetToken('refresh', MockTokenFactory.CreateValid()); @@ -185,22 +199,46 @@ export class JwtServiceTests { } @test public 'CurrentUser should return BuiltIn\\Visitor by default'() { - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); expect(t.CurrentUser).to.be.eq('BuiltIn\\Visitor'); } @test public 'CurrentUser should return user from payload when access token is set and valid'() { - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + this._repo.Config.JwtTokenPersist = 'expiration'; + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('access', MockTokenFactory.CreateValid()); expect(t.CurrentUser).to.be.eq('BuiltIn\\Mock'); } @test public 'CurrentUser should return user from payload when refresh token is set and valid'() { - const t = new JwtService(this._httpProvider, this._hostUrl, this._tokenTemplate, 'expiration'); + const t = new JwtService(this._repo); const store = t['_tokenStore'] as TokenStore; store.SetToken('refresh', MockTokenFactory.CreateValid()); expect(t.CurrentUser).to.be.eq('BuiltIn\\Mock'); } + @test public 'SetOauthProvider should add an Oauth provider'() { + const t = new JwtService(this._repo); + const provider = new MockOauthProvider(); + t.SetOauthProvider(provider); + + expect(t.GetOauthProvider(MockOauthProvider)).to.be.eq(provider); + } + + @test public 'SetOauthProvider should throw an error when for duplicated providers'() { + const t = new JwtService(this._repo); + const provider = new MockOauthProvider(); + const provider2 = new MockOauthProvider(); + t.SetOauthProvider(provider); + + expect(() => {t.SetOauthProvider(provider2); }).to.throw(); + } + + @test public 'GetOauthProvider should throw an error if there is no Oauth provider registered'() { + const t = new JwtService(this._repo); + expect(() => {t.GetOauthProvider(MockOauthProvider); }).to.throw(); + } + } diff --git a/test/Mocks/MockAuthService.ts b/test/Mocks/MockAuthService.ts index 460868b..715ab62 100644 --- a/test/Mocks/MockAuthService.ts +++ b/test/Mocks/MockAuthService.ts @@ -1,13 +1,33 @@ /** * @module Mocks */ /** */ -import { BehaviorSubject, } from 'rxjs/BehaviorSubject'; +import { BehaviorSubject, } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Observable'; import { ReplaySubject } from 'rxjs/ReplaySubject'; -import { IAuthenticationService, LoginState } from '../../src/Authentication'; +import { IAuthenticationService, IOauthProvider, LoginResponse, LoginState } from '../../src/Authentication'; export class MockAuthService implements IAuthenticationService { + public HandleAuthenticationResponse(response: LoginResponse, resp: boolean = true): boolean { + return resp; + } + private _oauthProviders: Map<{ new(...args: any[]): IOauthProvider }, IOauthProvider> = new Map(); + + public SetOauthProvider(provider: T) { + const providerCtor = provider.constructor as { new(...args: any[]): T }; + if (this._oauthProviders.has(providerCtor)) { + throw Error(`Provider for '${providerCtor.name}' already set`); + } + this._oauthProviders.set(providerCtor, provider); + } + + public GetOauthProvider(providerType: { new(...args: any[]): T }): T { + if (!this._oauthProviders.has(providerType)) { + throw Error(`OAuth provider not found for '${providerType.name}'`); + } + return this._oauthProviders.get(providerType) as T; + } + public CurrentUser: string = 'BuiltIn\\Visitor'; public StateSubject: BehaviorSubject; diff --git a/test/ODataApiTests.ts b/test/ODataApiTests.ts index 4ca45c7..8c90c2d 100644 --- a/test/ODataApiTests.ts +++ b/test/ODataApiTests.ts @@ -136,12 +136,6 @@ export const ODataApiTests = describe('ODataApi', () => { }); describe('#Upload()', () => { - it('requests to upload a Content and returns an Observable object', () => { - expect(typeof odataApi.Upload('/workspaces/Project', {}, false)).to.be.eq('object'); - }); - it('requests to upload a Content and returns an Observable object', () => { - expect(typeof odataApi.Upload('/workspaces/Project', {}, true)).to.be.eq('object'); - }); it('Should insert a Slash after OData.Svc for custom actions, if missing ', (done) => { const http = service.HttpProviderRef; diff --git a/test/RepositoryTests.ts b/test/RepositoryTests.ts index 2f74767..9b45ef2 100644 --- a/test/RepositoryTests.ts +++ b/test/RepositoryTests.ts @@ -454,11 +454,11 @@ export class RepositoryTests { this._repo.Config.ChunkSize = 4; this._repo.Authentication.StateSubject.next(LoginState.Authenticated); this._repo.HttpProviderRef - .AddResponse('9865*chunk-token*true*true') // first upload - .AddResponse({}) // Mocked chunks + .AddResponse('9865*chunk-token*true*true') // first upload + .AddResponse({}) // Mocked chunks .AddResponse({}) .AddResponse({}) - .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); // Content reload; + .AddResponse({ d: { Id: 12356, Path: 'Root/Test/alma' } }); // Content reload const testContent = this._repo.HandleLoadedContent({ Id: 12345, Path: 'Root/Test', Name: 'task' }); @@ -775,7 +775,7 @@ export class RepositoryTests { repo.GetCurrentUser().subscribe((u) => { done('Error should be thrown here.'); }, (err) => { - expect(err).to.be.eq("Error getting current user: found multiple users with login name 'NewUser' in domain 'BuiltIn'"); + expect(err).to.be.eq("Error getting current user: found 2 user(s) with login name 'NewUser' in domain 'BuiltIn'"); done(); }); From f0c475f04e890ac767c9906733314acd71b99ed2 Mon Sep 17 00:00:00 2001 From: gallayl Date: Fri, 24 Nov 2017 10:44:06 +0100 Subject: [PATCH 4/4] [KFI]chore(package): bumped version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78a98a7..c69002c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sn-client-js", - "version": "3.0.0-development.7", + "version": "3.0.0", "description": "A JavaScript client for Sense/Net ECM that makes it easy to use the REST API of the Content Repository.", "main": "dist/src/SN.js", "files": [