Search wp updates with various new functionalities (#27)
This commit is contained in:
parent
67795ba337
commit
b3812d76c4
|
@ -22,6 +22,7 @@ react-search-wp|Elio Struyf (MVP, Ventigrate, [@eliostruyf](https://twitter.com/
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
0.0.4|September 08, 2016|Initial release
|
0.0.4|September 08, 2016|Initial release
|
||||||
|
0.0.5|September 27, 2016|Updates for drop 4. Added the abilty to use various search tokens. Plus a logging field to watch search calls.
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
@ -44,6 +45,8 @@ The search web part is a sample client-side web part built on the SharePoint Fra
|
||||||
|
|
||||||
The web part has built in templating support for internal (created within the project) and external (loaded from a URL) templates.
|
The web part has built in templating support for internal (created within the project) and external (loaded from a URL) templates.
|
||||||
|
|
||||||
|
When adding your query you are able to make use of the following tokens: {Today}, {Today+Number}, {Today-Number}, {CurrentDisplayLanguage}, {User}, {User.Name}, {User.Email}, {Site}, {SiteCollection}.
|
||||||
|
|
||||||
**Internal templates**
|
**Internal templates**
|
||||||
|
|
||||||
Internal templates can be found in the [templates]('./src/webparts/templates') folder. You can start building your own templates by using one of the provided samples.
|
Internal templates can be found in the [templates]('./src/webparts/templates') folder. You can start building your own templates by using one of the provided samples.
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "search-wp-spfx",
|
"name": "search-wp-spfx",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/sp-client-base": "~0.2.0",
|
"@microsoft/sp-client-base": "~0.3.0",
|
||||||
"@microsoft/sp-client-preview": "~0.2.0",
|
"@microsoft/sp-client-preview": "~0.4.0",
|
||||||
"flux": "^2.1.1",
|
"flux": "^2.1.1",
|
||||||
"moment": "^2.14.1",
|
"moment": "^2.14.1",
|
||||||
"office-ui-fabric-react": "0.36.0",
|
"office-ui-fabric-react": "0.36.0",
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
"react-dom": "0.14.8"
|
"react-dom": "0.14.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/sp-build-web": "~0.5.0",
|
"@microsoft/sp-build-web": "~0.6.0",
|
||||||
"@microsoft/sp-module-interfaces": "~0.2.0",
|
"@microsoft/sp-module-interfaces": "~0.3.0",
|
||||||
"@microsoft/sp-webpart-workbench": "~0.2.0",
|
"@microsoft/sp-webpart-workbench": "~0.4.0",
|
||||||
"expose-loader": "^0.7.1",
|
"expose-loader": "^0.7.1",
|
||||||
"gulp": "~3.9.1"
|
"gulp": "~3.9.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IPropertyPaneLoggingFieldProps {
|
||||||
|
label?: string;
|
||||||
|
description?: string;
|
||||||
|
value: any;
|
||||||
|
retrieve?: Function;
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IPropertyPaneField,
|
||||||
|
IPropertyPaneFieldType
|
||||||
|
} from '@microsoft/sp-client-preview';
|
||||||
|
|
||||||
|
import { IPropertyPaneLoggingFieldProps } from './IPropertyPaneLoggingFieldProps';
|
||||||
|
import PropertyPaneLoggingFieldHost, { IPropertyPaneLoggingFieldHostProps } from './PropertyPaneLoggingFieldHost';
|
||||||
|
|
||||||
|
export interface IPropertyPaneLoggingFieldPropsInternal extends IPropertyPaneLoggingFieldProps {
|
||||||
|
onRender(elem: HTMLElement): void;
|
||||||
|
onDispose(elem: HTMLElement): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PropertyPaneLoggingFieldBuilder implements IPropertyPaneField<IPropertyPaneLoggingFieldPropsInternal> {
|
||||||
|
// Properties defined by IPropertyPaneField
|
||||||
|
public type: IPropertyPaneFieldType = IPropertyPaneFieldType.Custom;
|
||||||
|
public targetProperty: string = undefined;
|
||||||
|
public properties: IPropertyPaneLoggingFieldPropsInternal;
|
||||||
|
|
||||||
|
// Logging properties
|
||||||
|
private label: string;
|
||||||
|
private description: string;
|
||||||
|
private value: any;
|
||||||
|
private retrieve: Function;
|
||||||
|
|
||||||
|
public constructor(props: IPropertyPaneLoggingFieldPropsInternal) {
|
||||||
|
this.properties = props;
|
||||||
|
this.properties.onDispose = this.dispose;
|
||||||
|
this.properties.onRender = this.render;
|
||||||
|
|
||||||
|
this.label = props.label;
|
||||||
|
this.value = props.value;
|
||||||
|
this.description = props.description;
|
||||||
|
this.retrieve = props.retrieve;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Render the logging element
|
||||||
|
*/
|
||||||
|
private render(elm: HTMLElement): void {
|
||||||
|
// Construct the JSX properties
|
||||||
|
const element: React.ReactElement<IPropertyPaneLoggingFieldHostProps> = React.createElement(PropertyPaneLoggingFieldHost, {
|
||||||
|
label: this.label,
|
||||||
|
value: this.value,
|
||||||
|
description: this.description,
|
||||||
|
retrieve: this.retrieve,
|
||||||
|
onDispose: this.dispose,
|
||||||
|
onRender: this.render
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calls the REACT content generator
|
||||||
|
ReactDom.render(element, elm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Disposes the current object
|
||||||
|
*/
|
||||||
|
private dispose(elem: HTMLElement): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function PropertyPaneLoggingField(properties: IPropertyPaneLoggingFieldProps): IPropertyPaneField<IPropertyPaneLoggingFieldPropsInternal> {
|
||||||
|
// Create an internal properties object from the given properties
|
||||||
|
var newProperties: IPropertyPaneLoggingFieldPropsInternal = {
|
||||||
|
label: properties.label,
|
||||||
|
description: properties.description,
|
||||||
|
value: properties.value,
|
||||||
|
retrieve: properties.retrieve,
|
||||||
|
onDispose: null,
|
||||||
|
onRender: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calles the PropertyPaneLoggingField builder object
|
||||||
|
// This object will simulate a PropertyFieldCustom to manage his rendering process
|
||||||
|
return new PropertyPaneLoggingFieldBuilder(newProperties);
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { IPropertyPaneLoggingFieldPropsInternal } from './PropertyPaneLoggingField';
|
||||||
|
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||||
|
|
||||||
|
require('./PropertyPaneLoggingFieldStyling.css');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @interface
|
||||||
|
* PropertyPaneLoggingFieldHost properties interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface IPropertyPaneLoggingFieldHostProps extends IPropertyPaneLoggingFieldPropsInternal {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @interface
|
||||||
|
* PropertyPaneLoggingFieldHost state interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface IPropertyPaneLoggingFieldState {
|
||||||
|
logging?: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* Renders the controls for PropertyPaneLoggingField component
|
||||||
|
*/
|
||||||
|
export default class PropertyPaneLoggingFieldHost extends React.Component<IPropertyPaneLoggingFieldHostProps, IPropertyPaneLoggingFieldState> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Contructor
|
||||||
|
*/
|
||||||
|
constructor(props: IPropertyPaneLoggingFieldHostProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
logging: []
|
||||||
|
};
|
||||||
|
this.getLogging = this.getLogging.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* componentDidMount
|
||||||
|
*/
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.setState({
|
||||||
|
logging: this.props.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Retrieve new logging value
|
||||||
|
*/
|
||||||
|
private getLogging() {
|
||||||
|
this.setState({
|
||||||
|
logging: this.props.retrieve()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Renders the key values
|
||||||
|
*/
|
||||||
|
private renderValue(val: any, subClass?: string) {
|
||||||
|
const output = [];
|
||||||
|
for (const k in val) {
|
||||||
|
if (typeof val[k] === "object") {
|
||||||
|
output.push(<div key={k} className={subClass}><span className="keyValue">{k}</span>: object {this.renderValue(val[k], "subElm")}</div>);
|
||||||
|
} else {
|
||||||
|
output.push(<div key={k} className={subClass}><span className="keyValue">{k}</span>: {val[k]}</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function
|
||||||
|
* Renders the logging field control
|
||||||
|
*/
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const valToRender = this.renderValue(this.state.logging);
|
||||||
|
//Renders content
|
||||||
|
return (
|
||||||
|
<div className="loggingField">
|
||||||
|
<Label>{this.props.label}</Label>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
if (typeof this.props.retrieve !== 'undefined') {
|
||||||
|
return <div className="updateLogging"><a className="ms-Link" onClick={this.getLogging}>Update logging</a></div>;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
<pre className="logging">{valToRender}</pre>
|
||||||
|
{
|
||||||
|
(() => {
|
||||||
|
if (typeof this.props.description !== 'undefined') {
|
||||||
|
return <span className="ms-TextField-description">{this.props.description}</span>;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
.loggingField {
|
||||||
|
margin: 0 0 85px 0;
|
||||||
|
|
||||||
|
pre.logging {
|
||||||
|
border: 1px solid #c8c8c8;
|
||||||
|
margin: 0;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 6px;
|
||||||
|
white-space: initial;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
div {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subElm {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyValue {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.updateLogging {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
margin-top: -22px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
a.ms-Link {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import {
|
||||||
PropertyPaneToggle
|
PropertyPaneToggle
|
||||||
} from '@microsoft/sp-client-preview';
|
} from '@microsoft/sp-client-preview';
|
||||||
|
|
||||||
|
import { PropertyPaneLoggingField } from './PropertyPaneControls/PropertyPaneLoggingField';
|
||||||
|
|
||||||
import ModuleLoader from '@microsoft/sp-module-loader';
|
import ModuleLoader from '@microsoft/sp-module-loader';
|
||||||
|
|
||||||
import * as strings from 'mystrings';
|
import * as strings from 'mystrings';
|
||||||
|
@ -19,15 +21,24 @@ import { IExternalTemplate, IScripts, IStyles } from './utils/ITemplates';
|
||||||
import { defer, IDeferred } from './utils/defer';
|
import { defer, IDeferred } from './utils/defer';
|
||||||
import { allTemplates } from './templates/TemplateLoader';
|
import { allTemplates } from './templates/TemplateLoader';
|
||||||
|
|
||||||
|
// Import the search store, needed for logging the search requests
|
||||||
|
import searchStore from './flux/stores/searchStore';
|
||||||
|
|
||||||
// Expose React to window -> required for external template loading
|
// Expose React to window -> required for external template loading
|
||||||
require("expose?React!react");
|
require("expose?React!react");
|
||||||
|
|
||||||
export default class SearchSpfxWebPart extends BaseClientSideWebPart<ISearchSpfxWebPartProps> {
|
export default class SearchSpfxWebPart extends BaseClientSideWebPart<ISearchSpfxWebPartProps> {
|
||||||
private crntExternalTemplateUrl: string = "";
|
private crntExternalTemplateUrl: string = "";
|
||||||
private crntExternalTemplate: IExternalTemplate = null;
|
private crntExternalTemplate: IExternalTemplate = null;
|
||||||
|
private onChangeBinded: boolean = false;
|
||||||
|
private removeChangeBinding: NodeJS.Timer = null;
|
||||||
|
|
||||||
public constructor(context: IWebPartContext) {
|
public constructor(context: IWebPartContext) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
|
// Bind this to the setLogging method
|
||||||
|
this.setLogging = this.setLogging.bind(this);
|
||||||
|
this.removeLogging = this.removeLogging.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,6 +170,41 @@ export default class SearchSpfxWebPart extends BaseClientSideWebPart<ISearchSpfx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onPropertyPaneRendered(): void {
|
||||||
|
// Clear remove binding timeout. This is necessary if user applied a new configuration.
|
||||||
|
if (this.removeChangeBinding !== null) {
|
||||||
|
clearTimeout(this.removeChangeBinding);
|
||||||
|
this.removeChangeBinding = null;
|
||||||
|
}
|
||||||
|
// Check if there is a change binding in place
|
||||||
|
if (!this.onChangeBinded) {
|
||||||
|
this.onChangeBinded = true;
|
||||||
|
searchStore.addChangeListener(this.setLogging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Will probably be renamed to onPropertyConfigurationComplete in the next drop
|
||||||
|
protected onPropertyPaneConfigurationComplete() {
|
||||||
|
// Remove the change binding
|
||||||
|
this.removeChangeBinding = setTimeout(this.removeLogging, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected onPropertyPaneConfigurationStart() {
|
||||||
|
// // Will probably be deleted in the next drop
|
||||||
|
// console.log('onPropertyPaneConfigurationStart');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected onAfterPropertyPaneChangesApplied() {
|
||||||
|
// // Will probably be deleted in the next drop
|
||||||
|
// console.log('onAfterPropertyPaneChangesApplied');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Will probably be added in the next drop
|
||||||
|
// protected onPropertyPaneSave() {
|
||||||
|
// console.log('onPropertyPaneSave');
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property pane settings
|
* Property pane settings
|
||||||
*/
|
*/
|
||||||
|
@ -200,17 +246,57 @@ export default class SearchSpfxWebPart extends BaseClientSideWebPart<ISearchSpfx
|
||||||
}),
|
}),
|
||||||
PropertyPaneTextField('sorting', {
|
PropertyPaneTextField('sorting', {
|
||||||
label: strings.FieldsSorting
|
label: strings.FieldsSorting
|
||||||
}),
|
})
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
groupName: strings.TemplateGroupName,
|
||||||
|
groupFields: [
|
||||||
PropertyPaneToggle('external', {
|
PropertyPaneToggle('external', {
|
||||||
label: strings.FieldsExternalLabel
|
label: strings.FieldsExternalLabel
|
||||||
}),
|
}),
|
||||||
templateProperty
|
templateProperty
|
||||||
]
|
]
|
||||||
}]
|
}, {
|
||||||
|
groupName: strings.LoggingGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneLoggingField({
|
||||||
|
label: strings.LoggingFieldLabel,
|
||||||
|
description: strings.LoggingFieldDescription,
|
||||||
|
value: searchStore.getLoggingInfo(),
|
||||||
|
retrieve: this.getLogging
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
displayGroupsAsAccordion: true
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to retrieve the logging value from the store
|
||||||
|
*/
|
||||||
|
private getLogging(): any {
|
||||||
|
return searchStore.getLoggingInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to refresh the property pane when a change is retrieved from the store
|
||||||
|
*/
|
||||||
|
private setLogging(): void {
|
||||||
|
// Refresh the property pane when search rest call is completed
|
||||||
|
this.configureStart(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to remove the change binding when property pane is closed
|
||||||
|
*/
|
||||||
|
private removeLogging(): void {
|
||||||
|
if (this.onChangeBinded) {
|
||||||
|
this.onChangeBinded = false;
|
||||||
|
searchStore.removeChangeListener(this.setLogging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent from changing the query on typing
|
* Prevent from changing the query on typing
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
||||||
|
import { IPageContext } from '../../utils/IPageContext';
|
||||||
|
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
declare const _spPageContextInfo: IPageContext;
|
||||||
|
|
||||||
|
export default class SearchTokenHelper {
|
||||||
|
private regexVal: RegExp = /\{[^\{]*?\}/gi;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
public replaceTokens(restUrl: string, context: IWebPartContext): string {
|
||||||
|
const tokens = restUrl.match(this.regexVal);
|
||||||
|
|
||||||
|
if (tokens !== null && tokens.length > 0) {
|
||||||
|
tokens.forEach((token) => {
|
||||||
|
// Check which token has been retrieved
|
||||||
|
if (token.toLowerCase().indexOf('today') !== -1) {
|
||||||
|
const dateValue = this.getDateValue(token);
|
||||||
|
restUrl = restUrl.replace(token, dateValue);
|
||||||
|
}
|
||||||
|
else if (token.toLowerCase().indexOf('user') !== -1) {
|
||||||
|
const userValue = this.getUserValue(token, context);
|
||||||
|
restUrl = restUrl.replace(token, userValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (token.toLowerCase()) {
|
||||||
|
case "{site}":
|
||||||
|
restUrl = restUrl.replace(/{site}/ig, context.pageContext.web.absoluteUrl);
|
||||||
|
break;
|
||||||
|
case "{sitecollection}":
|
||||||
|
restUrl = restUrl.replace(/{sitecollection}/ig, _spPageContextInfo.siteAbsoluteUrl);
|
||||||
|
break;
|
||||||
|
case "{currentdisplaylanguage}":
|
||||||
|
restUrl = restUrl.replace(/{currentdisplaylanguage}/ig, context.pageContext.cultureInfo.currentCultureName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return restUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDateValue(token: string): string {
|
||||||
|
let dateValue = moment();
|
||||||
|
// Check if we need to add days
|
||||||
|
if (token.toLowerCase().indexOf("{today+") !== -1) {
|
||||||
|
const daysVal = this.getDaysVal(token);
|
||||||
|
dateValue = dateValue.add(daysVal, 'day');
|
||||||
|
}
|
||||||
|
// Check if we need to subtract days
|
||||||
|
if (token.toLowerCase().indexOf("{today-") !== -1) {
|
||||||
|
const daysVal = this.getDaysVal(token);
|
||||||
|
dateValue = dateValue.subtract(daysVal, 'day');
|
||||||
|
}
|
||||||
|
return dateValue.format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDaysVal(token: string): number {
|
||||||
|
const tmpDays: string = token.substring(7, token.length - 1);
|
||||||
|
return parseInt(tmpDays) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUserValue(token: string, context: IWebPartContext): string {
|
||||||
|
let userValue = '"' + context.pageContext.user.displayName + '"';
|
||||||
|
|
||||||
|
if (token.toLowerCase().indexOf("{user.") !== -1) {
|
||||||
|
const propVal = token.toLowerCase().substring(6, token.length - 1);
|
||||||
|
switch (propVal) {
|
||||||
|
case "name":
|
||||||
|
userValue = '"' + context.pageContext.user.displayName + '"';
|
||||||
|
break;
|
||||||
|
case "email":
|
||||||
|
userValue = context.pageContext.user.email;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userValue;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
import appDispatcher from '../dispatcher/appDispatcher';
|
import appDispatcher from '../dispatcher/appDispatcher';
|
||||||
import searchActionIDs from '../actions/searchActionIDs';
|
import searchActionIDs from '../actions/searchActionIDs';
|
||||||
|
import SearchTokenHelper from '../helpers/SearchTokenHelper';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
import { IWebPartContext } from '@microsoft/sp-client-preview';
|
||||||
import { ISearchResults, ICells, ICellValue } from '../../utils/ISearchResults';
|
import { ISearchResults, ICells, ICellValue } from '../../utils/ISearchResults';
|
||||||
import { IPageContext } from '../../utils/IPageContext';
|
|
||||||
|
|
||||||
declare const _spPageContextInfo: IPageContext;
|
|
||||||
|
|
||||||
const CHANGE_EVENT: string = 'change';
|
const CHANGE_EVENT: string = 'change';
|
||||||
|
|
||||||
export class SearchStoreStatic extends EventEmitter {
|
export class SearchStoreStatic extends EventEmitter {
|
||||||
private _results: any[] = [];
|
private _results: any[] = [];
|
||||||
|
private _url: string;
|
||||||
|
private _response: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
|
@ -36,8 +36,8 @@ export class SearchStoreStatic extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSearchResults(crntResults: ICells[], fields: string): void {
|
public setSearchResults(crntResults: ICells[], fields: string): void {
|
||||||
const flds: string[] = fields.toLowerCase().split(',');
|
|
||||||
if (crntResults.length > 0) {
|
if (crntResults.length > 0) {
|
||||||
|
const flds: string[] = fields.toLowerCase().split(',');
|
||||||
const temp: any[] = [];
|
const temp: any[] = [];
|
||||||
crntResults.forEach((result) => {
|
crntResults.forEach((result) => {
|
||||||
// Create a temp value
|
// Create a temp value
|
||||||
|
@ -73,19 +73,6 @@ export class SearchStoreStatic extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} query
|
|
||||||
*/
|
|
||||||
public ReplaceTokens (query: string, context: IWebPartContext): string {
|
|
||||||
if (query.toLowerCase().indexOf("{site}") !== -1) {
|
|
||||||
query = query.replace(/{site}/ig, context.pageContext.web.absoluteUrl);
|
|
||||||
}
|
|
||||||
if (query.toLowerCase().indexOf("{sitecollection}") !== -1) {
|
|
||||||
query = query.replace(/{sitecollection}/ig, _spPageContextInfo.siteAbsoluteUrl);
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
|
@ -99,6 +86,18 @@ export class SearchStoreStatic extends EventEmitter {
|
||||||
public isNull (value: any): boolean {
|
public isNull (value: any): boolean {
|
||||||
return value === null || typeof value === "undefined";
|
return value === null || typeof value === "undefined";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setLoggingInfo(url: string, response: any) {
|
||||||
|
this._url = url;
|
||||||
|
this._response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLoggingInfo(): any {
|
||||||
|
return {
|
||||||
|
URL: this._url,
|
||||||
|
Response: this._response
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchStore: SearchStoreStatic = new SearchStoreStatic();
|
const searchStore: SearchStoreStatic = new SearchStoreStatic();
|
||||||
|
@ -106,9 +105,10 @@ const searchStore: SearchStoreStatic = new SearchStoreStatic();
|
||||||
appDispatcher.register((action) => {
|
appDispatcher.register((action) => {
|
||||||
switch (action.actionType) {
|
switch (action.actionType) {
|
||||||
case searchActionIDs.SEARCH_GET:
|
case searchActionIDs.SEARCH_GET:
|
||||||
|
const tokenHelper = new SearchTokenHelper();
|
||||||
let url: string = action.context.pageContext.web.absoluteUrl + "/_api/search/query?querytext=";
|
let url: string = action.context.pageContext.web.absoluteUrl + "/_api/search/query?querytext=";
|
||||||
// Check if a query is provided
|
// Check if a query is provided
|
||||||
url += !searchStore.isEmptyString(action.query) ? `'${searchStore.ReplaceTokens(action.query, action.context)}'` : "'*'";
|
url += !searchStore.isEmptyString(action.query) ? `'${tokenHelper.replaceTokens(action.query, action.context)}'` : "'*'";
|
||||||
// Check if there are fields provided
|
// Check if there are fields provided
|
||||||
url += '&selectproperties=';
|
url += '&selectproperties=';
|
||||||
url += !searchStore.isEmptyString(action.fields) ? `'${action.fields}'` : "'path,title'";
|
url += !searchStore.isEmptyString(action.fields) ? `'${action.fields}'` : "'path,title'";
|
||||||
|
@ -121,20 +121,28 @@ appDispatcher.register((action) => {
|
||||||
url += "&clienttype='ContentSearchRegular'";
|
url += "&clienttype='ContentSearchRegular'";
|
||||||
|
|
||||||
searchStore.GetSearchData(action.context, url).then((res: ISearchResults) => {
|
searchStore.GetSearchData(action.context, url).then((res: ISearchResults) => {
|
||||||
|
searchStore.setLoggingInfo(url, res);
|
||||||
|
let resultsRetrieved = false;
|
||||||
if (res !== null) {
|
if (res !== null) {
|
||||||
if (typeof res.PrimaryQueryResult !== 'undefined') {
|
if (typeof res.PrimaryQueryResult !== 'undefined') {
|
||||||
if (typeof res.PrimaryQueryResult.RelevantResults !== 'undefined') {
|
if (typeof res.PrimaryQueryResult.RelevantResults !== 'undefined') {
|
||||||
if (typeof res.PrimaryQueryResult.RelevantResults !== 'undefined') {
|
if (typeof res.PrimaryQueryResult.RelevantResults !== 'undefined') {
|
||||||
if (typeof res.PrimaryQueryResult.RelevantResults.Table !== 'undefined') {
|
if (typeof res.PrimaryQueryResult.RelevantResults.Table !== 'undefined') {
|
||||||
if (typeof res.PrimaryQueryResult.RelevantResults.Table.Rows !== 'undefined') {
|
if (typeof res.PrimaryQueryResult.RelevantResults.Table.Rows !== 'undefined') {
|
||||||
|
resultsRetrieved = true;
|
||||||
searchStore.setSearchResults(res.PrimaryQueryResult.RelevantResults.Table.Rows, action.fields);
|
searchStore.setSearchResults(res.PrimaryQueryResult.RelevantResults.Table.Rows, action.fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the store its search result set on error
|
||||||
|
if (!resultsRetrieved) {
|
||||||
|
searchStore.setSearchResults([], null);
|
||||||
|
}
|
||||||
searchStore.emitChange();
|
searchStore.emitChange();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -8,8 +8,12 @@ define([], function() {
|
||||||
"FieldsTemplateLabel": "Choose the template you want to use for rendering the results",
|
"FieldsTemplateLabel": "Choose the template you want to use for rendering the results",
|
||||||
"FieldsMaxResults": "Number of results to render",
|
"FieldsMaxResults": "Number of results to render",
|
||||||
"FieldsSorting": "Sorting (MP:ascending or descending) - example: lastmodifiedtime:ascending,author:descending",
|
"FieldsSorting": "Sorting (MP:ascending or descending) - example: lastmodifiedtime:ascending,author:descending",
|
||||||
"QueryInfoDescription": "You can make use of following tokens: {Site} - {SiteCollection}",
|
"QueryInfoDescription": "You can make use of following tokens: {Site} - {SiteCollection} - {Today} or {Today+NR} or {Today-NR} - {CurrentDisplayLanguage} - {User}, {User.Name}, {User.Email}",
|
||||||
"FieldsExternalLabel": "Do you want to use an external template?",
|
"FieldsExternalLabel": "Do you want to use an external template?",
|
||||||
"FieldsExternalTempLabel": "Specify the URL of the external template"
|
"FieldsExternalTempLabel": "Specify the URL of the external template",
|
||||||
|
"TemplateGroupName": "Template settings",
|
||||||
|
"LoggingGroupName": "Logging pane",
|
||||||
|
"LoggingFieldLabel": "Logging search API calls",
|
||||||
|
"LoggingFieldDescription": "This field logs all search API calls"
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -10,6 +10,10 @@ declare interface IStrings {
|
||||||
QueryInfoDescription: string;
|
QueryInfoDescription: string;
|
||||||
FieldsExternalLabel: string;
|
FieldsExternalLabel: string;
|
||||||
FieldsExternalTempLabel: string;
|
FieldsExternalTempLabel: string;
|
||||||
|
TemplateGroupName: string;
|
||||||
|
LoggingGroupName: string;
|
||||||
|
LoggingFieldLabel: string;
|
||||||
|
LoggingFieldDescription: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'mystrings' {
|
declare module 'mystrings' {
|
||||||
|
|
Loading…
Reference in New Issue