├── .npmignore
├── .gitignore
├── img
└── generator-init.png
├── generators
├── spsync
│ ├── templates
│ │ ├── _package.json
│ │ ├── settings.js
│ │ └── _gulpfile.js
│ └── index.js
├── spsync-creds
│ ├── templates
│ │ ├── _package.json
│ │ ├── settings.js
│ │ └── _gulpfile.js
│ └── index.js
└── app
│ ├── templates
│ ├── sample
│ │ ├── Control_Starter.html
│ │ ├── Item_Starter.html
│ │ ├── Control_Minimal.js
│ │ └── Item_Minimal.js
│ └── config.json
│ └── index.js
├── package.json
├── LICENSE
└── README.md
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.tgz
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | *.tgz
--------------------------------------------------------------------------------
/img/generator-init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/estruyf/generator-displaytemplates/HEAD/img/generator-init.png
--------------------------------------------------------------------------------
/generators/spsync/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "displaytemplates",
3 | "version": "0.1.0",
4 | "description": "Developing display templates via the gulp-spsync plugin",
5 | "dependencies": {
6 | "gulp": "^3.9.1",
7 | "gulp-watch": "^4.3.9",
8 | "gulp-spsync": "^1.5.3"
9 | },
10 | "license": "UNLICENSED",
11 | "private": true
12 | }
--------------------------------------------------------------------------------
/generators/spsync-creds/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "displaytemplates",
3 | "version": "0.1.0",
4 | "description": "Developing display templates via the gulp-spsync-creds plugin",
5 | "dependencies": {
6 | "gulp": "^3.9.1",
7 | "gulp-spsync-creds": "^2.2.6",
8 | "gulp-plumber": "^1.1.0"
9 | },
10 | "license": "UNLICENSED",
11 | "private": true
12 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-displaytemplates",
3 | "version": "1.2.0",
4 | "description": "This is a Yeoman generator to speed up your display templates development process",
5 | "author": "Elio Struyf",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/estruyf/generator-displaytemplates.git"
9 | },
10 | "bugs": {
11 | "url": "https://github.com/estruyf/generator-displaytemplates/issues"
12 | },
13 | "homepage": "https://github.com/estruyf/generator-displaytemplates#readme",
14 | "files": [
15 | "generators"
16 | ],
17 | "keywords": [
18 | "yeoman-generator"
19 | ],
20 | "dependencies": {
21 | "chalk": "1.1.3",
22 | "inquirer": "1.1.3",
23 | "mkdirp": "0.5.1",
24 | "update-notifier": "1.0.2",
25 | "yeoman-generator": "0.24.1",
26 | "yosay": "1.2.0"
27 | },
28 | "licenses": [
29 | {
30 | "type": "MIT",
31 | "url": "http://www.opensource.org/licenses/mit-license.php"
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Elio Struyf
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/generators/spsync/templates/settings.js:
--------------------------------------------------------------------------------
1 | var settings = (function () {
2 | var config = require('./config.json');
3 |
4 | return{
5 | checks: function () {
6 | if (typeof config.client_id === 'undefined') {
7 | throw "client_id is required in the configuration file"
8 | }
9 | if (typeof config.client_secret === 'undefined') {
10 | throw "client_secret is required in the configuration file"
11 | }
12 | if (typeof config.site === 'undefined') {
13 | throw "site is required in the configuration file"
14 | }
15 | if (typeof config.fileMetadata === 'undefined') {
16 | config.fileMetadata = [];
17 | }
18 | },
19 | get: function () {
20 | this.checks();
21 | return {
22 | client_id: config.client_id,
23 | client_secret: config.client_secret,
24 | site: config.site,
25 | files_metadata: config.fileMetadata
26 | }
27 | },
28 | getWatch: function () {
29 | this.checks();
30 | return {
31 | client_id: config.client_id,
32 | client_secret: config.client_secret,
33 | site: config.site,
34 | watch: true,
35 | files_metadata: config.fileMetadata
36 | }
37 | }
38 | }
39 | })();
40 |
41 | module.exports = settings;
--------------------------------------------------------------------------------
/generators/spsync-creds/templates/settings.js:
--------------------------------------------------------------------------------
1 | var settings = (function () {
2 | var config = require('./config.json');
3 |
4 | return{
5 | checks: function () {
6 | if (typeof config.username === 'undefined') {
7 | throw "username is required in the configuration file"
8 | }
9 | if (typeof config.password === 'undefined') {
10 | throw "password is required in the configuration file"
11 | }
12 | if (typeof config.site === 'undefined') {
13 | throw "site is required in the configuration file"
14 | }
15 | if (typeof config.fileMetadata === 'undefined') {
16 | config.fileMetadata = [];
17 | }
18 | },
19 | get: function () {
20 | this.checks();
21 | return {
22 | username: config.username,
23 | password: config.password,
24 | site: config.site,
25 | files_metadata: config.fileMetadata
26 | }
27 | },
28 | download: function () {
29 | if (typeof config.location === 'undefined') {
30 | throw "location is required in the configuration file"
31 | }
32 | this.checks();
33 | return {
34 | username: config.username,
35 | password: config.password,
36 | site: config.site,
37 | startFolder: config.location,
38 | associatedHtml: false
39 | }
40 | }
41 | }
42 | })();
43 |
44 | module.exports = settings;
--------------------------------------------------------------------------------
/generators/spsync/templates/_gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var watch = require('gulp-watch');
3 | var sp = require('gulp-spsync');
4 | var settings = require('./settings');
5 |
6 | var folder = 'src/**/*.*';
7 |
8 | /*
9 | Default task: uploads all the files to SharePoint
10 | */
11 | gulp.task('default', function() {
12 | return gulp.src(folder)
13 | .pipe(sp(settings.get()));
14 | });
15 |
16 | /*
17 | set-metadata task: uploads all the files to SharePoint and overwrites the metadata setting
18 | */
19 | gulp.task('set-metadata', function() {
20 | var crntSettings = settings.get();
21 | crntSettings["update_metadata"] = true;
22 |
23 | return gulp.src(folder)
24 | .pipe(sp(crntSettings));
25 | });
26 |
27 | /*
28 | publish task: uploads everything, sets metadata and publishes each file
29 | */
30 | gulp.task('publish', function() {
31 | var crntSettings = settings.get();
32 | crntSettings["update_metadata"] = true;
33 | crntSettings["publish"] = true;
34 |
35 | return gulp.src(folder)
36 | .pipe(sp(crntSettings));
37 | });
38 |
39 | /*
40 | watch task: this task can be used during the development process, it automatically uploads the files when changed
41 | */
42 | gulp.task("watch", function(){
43 | return gulp.src(folder)
44 | .pipe(watch(folder))
45 | .pipe(sp(settings.getWatch()));
46 | });
47 |
48 | /*
49 | watch task: this task can be used during the development process, it automatically uploads the files when changed and sets metadata
50 | */
51 | gulp.task("watch-metadata", function(){
52 | var crntSettings = settings.getWatch();
53 | crntSettings["update_metadata"] = true;
54 |
55 | return gulp.src(folder)
56 | .pipe(watch(folder))
57 | .pipe(sp(crntSettings));
58 | });
59 |
60 |
--------------------------------------------------------------------------------
/generators/app/templates/sample/Control_Starter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Control starter
4 |
5 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
44 |
45 |
46 | _#= ctx.RenderGroups(ctx) =#_
47 |
48 |
52 |
_#= $noResults =#_
53 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/generators/app/templates/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileMetadata": [
3 | {
4 | "name": "Item_Minimal.js",
5 | "metadata": {
6 | "__metadata": { "type": "SP.Data.OData__x005f_catalogs_x002f_masterpageItem" },
7 | "Title": "Item Minimal Template (via GULP)",
8 | "MasterPageDescription": "This is a display template added via gulp.",
9 | "ManagedPropertyMapping": "'Path','Title':'Title'",
10 | "ContentTypeId": "0x0101002039C03B61C64EC4A04F5361F38510660500A0383064C59087438E649B7323C95AF6",
11 | "DisplayTemplateLevel": "Item",
12 | "TemplateHidden": false,
13 | "TargetControlType": {
14 | "__metadata": {
15 | "type": "Collection(Edm.String)"
16 | },
17 | "results": [
18 | "SearchResults",
19 | "Content Web Parts"
20 | ]
21 | }
22 | }
23 | },
24 | {
25 | "name": "Control_Minimal.js",
26 | "metadata": {
27 | "__metadata": { "type": "SP.Data.OData__x005f_catalogs_x002f_masterpageItem" },
28 | "Title": "Control Minimal Template (via GULP)",
29 | "MasterPageDescription": "This is a display template added via gulp.",
30 | "ContentTypeId": "0x0101002039C03B61C64EC4A04F5361F38510660500A0383064C59087438E649B7323C95AF6",
31 | "DisplayTemplateLevel": "Control",
32 | "TemplateHidden": false,
33 | "TargetControlType": {
34 | "__metadata": {
35 | "type": "Collection(Edm.String)"
36 | },
37 | "results": [
38 | "SearchResults",
39 | "Content Web Parts"
40 | ]
41 | }
42 | }
43 | }
44 | ]
45 | }
--------------------------------------------------------------------------------
/generators/spsync-creds/templates/_gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp')
2 | var plumber = require("gulp-plumber");
3 | var sp = require('gulp-spsync-creds');
4 | var settings = require('./settings');
5 | var onError = function (err) {
6 | this.emit("end");
7 | };
8 |
9 | var mainFld = 'src';
10 | var folder = './src/**/*.*';
11 |
12 | /*
13 | Default task: uploads all the files to SharePoint
14 | */
15 | gulp.task('default', function() {
16 | return gulp.src(folder).pipe(
17 | sp.sync(settings.get())
18 | );
19 | });
20 |
21 | /*
22 | set-metadata task: uploads all the files to SharePoint and overwrites the metadata setting
23 | */
24 | gulp.task('set-metadata', function() {
25 | var crntSettings = settings.get();
26 | crntSettings["update_metadata"] = true;
27 |
28 | return gulp.src(folder)
29 | .pipe(sp.sync(crntSettings));
30 | });
31 |
32 | /*
33 | publish task: uploads everything, sets metadata and publishes each file
34 | */
35 | gulp.task('publish', function() {
36 | var crntSettings = settings.get();
37 | crntSettings["update_metadata"] = true;
38 | crntSettings["publish"] = true;
39 |
40 | return gulp.src(folder)
41 | .pipe(sp.sync(crntSettings));
42 | });
43 |
44 | /*
45 | watch task: this task can be used during the development process, it automatically uploads the files when changed
46 | */
47 | gulp.task("watch", function() {
48 | var crntSettings = settings.get();
49 | crntSettings["cache"] = true;
50 |
51 | gulp.watch(folder, function (event) {
52 | gulp.src(event.path, { base: mainFld })
53 | .pipe(plumber({
54 | errorHandler: onError
55 | }))
56 | .pipe(sp.sync(crntSettings));
57 | });
58 | });
59 |
60 | /*
61 | watch task: this task can be used during the development process, it automatically uploads the files when changed and sets metadata
62 | */
63 | gulp.task("watch-metadata", function(){
64 | var crntSettings = settings.get();
65 | crntSettings["update_metadata"] = true;
66 | crntSettings["cache"] = true;
67 |
68 | gulp.watch(folder, function (event) {
69 | gulp.src(event.path, { base: mainFld })
70 | .pipe(plumber({
71 | errorHandler: onError
72 | }))
73 | .pipe(sp.sync(crntSettings));
74 | });
75 | });
76 |
77 |
78 | /*
79 | download task: download the files for the specified folder
80 | */
81 | gulp.task('download', function() {
82 | var crntSettings = settings.download();
83 | return sp.download(crntSettings).pipe(gulp.dest("src/" + crntSettings.startFolder));
84 | });
--------------------------------------------------------------------------------
/generators/app/templates/sample/Item_Starter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Item starter
4 |
5 |
15 |
16 |
17 |
18 |
20 |
21 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/generators/app/templates/sample/Control_Minimal.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | // Config contains variables that are defined in one place
5 | var config = {
6 | /* IMPORTANT: update these settings before uploading the file to the master page gallery */
7 | template: 'control_minimal.js'
8 | };
9 | var templateUrl;
10 |
11 | var register = function () {
12 | if ("undefined" !== typeof (Srch) && "undefined" !== typeof (Srch.U) && typeof (Srch.U.registerRenderTemplateByName) === "function") {
13 | Srch.U.registerRenderTemplateByName(templateUrl, render);
14 | }
15 | },
16 | render = function (ctx) {
17 | // Display template data
18 | var cachePreviousTemplateData = ctx.DisplayTemplateData;
19 | ctx.DisplayTemplateData = {
20 | 'TemplateUrl': templateUrl,
21 | 'TemplateType': 'Control',
22 | 'TargetControlType': ['SearchResults', 'Content Web Parts']
23 | };
24 |
25 | // Checks to see if the client control loaded correctly
26 | if (!$isNull(ctx.ClientControl) && !$isNull(ctx.ClientControl.shouldRenderControl) && !ctx.ClientControl.shouldRenderControl()) {
27 | return "";
28 | }
29 |
30 | ctx.ListDataJSONGroupsKey = "ResultTables";
31 | ctx['ItemRenderWrapper'] = itemRendering;
32 |
33 | // HTML markup for the control template
34 | var htmlMarkup = String.format( '', ctx.RenderGroups(ctx));
37 |
38 | // Caching
39 | ctx.DisplayTemplateData = cachePreviousTemplateData;
40 |
41 | // Return the HTML markup
42 | return htmlMarkup;
43 | },
44 | itemRendering = function (itemRenderResult, inCtx, tpl) {
45 | var iStr = [];
46 | iStr.push('');
47 | iStr.push(itemRenderResult);
48 | iStr.push('');
49 | return iStr.join('');
50 | };
51 |
52 | /* DO NOT REMOVE THE FOLLOWING LINES OF CODE */
53 | // MDS needs to start on the head
54 | // Retrieve all the loaded scripts
55 | var allScripts = document.head.getElementsByTagName("script");
56 | var scriptUrl = null;
57 | var scriptNr = allScripts.length;
58 | while(scriptNr--) {
59 | var crntScript = allScripts[scriptNr];
60 | if (crntScript.src !== null) {
61 | // Check if the right script is retrieved based on the filename of the template
62 | if (crntScript.src.indexOf('/_catalogs/') > 0 && crntScript.src.toLowerCase().indexOf(config.template.toLowerCase()) > 0) {
63 | scriptUrl = crntScript.src;
64 | break;
65 | }
66 | }
67 | }
68 | if (scriptUrl !== null) {
69 | // Remove the query string
70 | if (scriptUrl.indexOf('?') > 0) {
71 | scriptUrl = scriptUrl.split("?")[0];
72 | }
73 | // Insert the site collection token
74 | templateUrl = '~sitecollection' + scriptUrl.substr(scriptUrl.indexOf('/_catalogs/'));
75 | templateUrl = decodeURI(templateUrl);
76 | // Register the template to load
77 | register();
78 | if (typeof (RegisterModuleInit) === "function" && typeof(Srch.U.replaceUrlTokens) === "function") {
79 | RegisterModuleInit(Srch.U.replaceUrlTokens(templateUrl), register);
80 | }
81 | }
82 | })();
--------------------------------------------------------------------------------
/generators/app/templates/sample/Item_Minimal.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | // Config contains variables that are defined in one place
5 | var config = {
6 | /* IMPORTANT: update these settings before uploading the file to the master page gallery */
7 | template: 'item_minimal.js',
8 | propertyMappings: { 'Path':null, 'Title':['Title'] }
9 | };
10 | var templateUrl;
11 |
12 | var register = function () {
13 | if ("undefined" !== typeof (Srch) && "undefined" !== typeof (Srch.U) && typeof (Srch.U.registerRenderTemplateByName) === "function") {
14 | Srch.U.registerRenderTemplateByName(templateUrl, render);
15 | }
16 | },
17 | render = function (ctx) {
18 | // Display template data
19 | var cachePreviousTemplateData = ctx.DisplayTemplateData;
20 | ctx.DisplayTemplateData = {
21 | 'TemplateUrl': templateUrl,
22 | 'TemplateType': 'Item',
23 | 'TargetControlType': ['SearchResults', 'Content Web Parts'],
24 | 'ManagedPropertyMapping': config.propertyMappings
25 | };
26 | var cachePreviousItemValuesFunction = ctx.ItemValues;
27 | ctx.ItemValues = function(slotOrPropName) {
28 | return Srch.ValueInfo.getCachedCtxItemValue(ctx, slotOrPropName);
29 | };
30 |
31 | // Retrieve managed property data
32 | var path = $getItemValue(ctx, 'Path');
33 | var title = $getItemValue(ctx, 'Title');
34 |
35 | // HTML markup for an item
36 | var htmlMarkup = String.format( '', path, title);
39 |
40 | // Caching
41 | ctx.ItemValues = cachePreviousItemValuesFunction;
42 | ctx.DisplayTemplateData = cachePreviousTemplateData;
43 |
44 | // Return the HTML markup
45 | return htmlMarkup;
46 | };
47 |
48 | /* DO NOT REMOVE THE FOLLOWING LINES OF CODE */
49 | // MDS needs to start on the head
50 | // Retrieve all the loaded scripts
51 | var allScripts = document.head.getElementsByTagName("script");
52 | var scriptUrl = null;
53 | var scriptNr = allScripts.length;
54 | while(scriptNr--) {
55 | var crntScript = allScripts[scriptNr];
56 | if (crntScript.src !== null) {
57 | // Check if the right script is retrieved based on the filename of the template
58 | if (crntScript.src.indexOf('/_catalogs/') > 0 && crntScript.src.toLowerCase().indexOf(config.template.toLowerCase()) > 0) {
59 | scriptUrl = crntScript.src;
60 | break;
61 | }
62 | }
63 | }
64 | if (scriptUrl !== null) {
65 | // Remove the query string
66 | if (scriptUrl.indexOf('?') > 0) {
67 | scriptUrl = scriptUrl.split("?")[0];
68 | }
69 | // Insert the site collection token
70 | templateUrl = '~sitecollection' + scriptUrl.substr(scriptUrl.indexOf('/_catalogs/'));
71 | templateUrl = decodeURI(templateUrl);
72 | // Register the template to load
73 | register();
74 | if (typeof (RegisterModuleInit) === "function" && typeof(Srch.U.replaceUrlTokens) === "function") {
75 | RegisterModuleInit(Srch.U.replaceUrlTokens(templateUrl), register);
76 | }
77 | }
78 | })();
--------------------------------------------------------------------------------
/generators/spsync/index.js:
--------------------------------------------------------------------------------
1 | var generators = require('yeoman-generator');
2 | var yosay = require('yosay');
3 | var chalk = require('chalk');
4 | var mkdirp = require('mkdirp');
5 |
6 | module.exports = generators.Base.extend({
7 | // The name constructor is important here
8 | constructor: function () {
9 | // Calling the super constructor is important so our generator is correctly set up
10 | generators.Base.apply(this, arguments);
11 |
12 | // Next, add your custom code
13 | this.option('name', {
14 | type: String,
15 | desc: 'Title of the Office Add-in',
16 | required: false
17 | });
18 |
19 | this.option('client_id', {
20 | type: String,
21 | desc: 'SharePoint add-in client id',
22 | required: true
23 | });
24 |
25 | this.option('client_secret', {
26 | type: String,
27 | desc: 'SharePoint add-in secret',
28 | required: true
29 | });
30 |
31 | this.option('site', {
32 | type: String,
33 | desc: 'SharePoint site URL',
34 | required: true
35 | });
36 |
37 | this.option('skipInstall', {
38 | type: Boolean,
39 | required: false,
40 | defaults: false,
41 | desc: 'Skip running NPM package manager at the end.'
42 | });
43 |
44 | this.genConfig = {};
45 | },
46 |
47 | prompting: {
48 | askFor: function () {
49 | var done = this.async();
50 |
51 | var prompts = [
52 | {
53 | name: 'client_id',
54 | message: 'What is your SharePoint add-in client id?',
55 | default: null, //00000000-0000-0000-0000-000000000000
56 | when: this.options.client_id === undefined
57 | },
58 | {
59 | name: 'client_secret',
60 | message: 'What is your SharePoint add-in secret?',
61 | default: null,
62 | when: this.options.client_secret === undefined
63 | }
64 | ];
65 |
66 | this.prompt(prompts, function(responses){
67 | this.genConfig = Object.assign({}, this.genConfig, this.options, responses);
68 | done();
69 | }.bind(this));
70 | }
71 | },
72 |
73 | writing: {
74 | app: function() {
75 | var done = this.async();
76 |
77 | // create common assets
78 | this.fs.copyTpl(this.templatePath('_gulpfile.js'),
79 | this.destinationPath('gulpfile.js'));
80 | this.fs.copyTpl(this.templatePath('_package.json'),
81 | this.destinationPath('package.json'));
82 | this.fs.copyTpl(this.templatePath('settings.js'),
83 | this.destinationPath('settings.js'));
84 |
85 | // Create and update the config file with the required properties
86 | var pathToConfigJson = this.templatePath('../../app/templates/config.json');
87 | if (this.fs.exists(pathToConfigJson)) {
88 | // Load config.json file
89 | var configJson = this.fs.readJSON(pathToConfigJson, 'utf8');
90 |
91 | // Set the required properties
92 | if (!configJson['client_id']) {
93 | configJson['client_id'] = this.genConfig.client_id;
94 | }
95 | if (!configJson['client_secret']) {
96 | configJson['client_secret'] = this.genConfig.client_secret;
97 | }
98 | if (!configJson['site']) {
99 | configJson['site'] = this.genConfig.site;
100 | }
101 | if (!configJson['skipInstall']) {
102 | configJson['skipInstall'] = this.genConfig.skipInstall;
103 | }
104 |
105 | // Overwrite the existing config.json
106 | this.log(chalk.green('Adding your configuration to the config.json file'));
107 | this.fs.writeJSON(this.destinationPath('config.json'), configJson);
108 | }
109 |
110 | done();
111 | }
112 | },
113 |
114 | install: function() {
115 | // Run npm installer?
116 | if (!this.genConfig['skipInstall']) {
117 | this.npmInstall();
118 | }
119 | }
120 | });
--------------------------------------------------------------------------------
/generators/spsync-creds/index.js:
--------------------------------------------------------------------------------
1 | var generators = require('yeoman-generator');
2 | var yosay = require('yosay');
3 | var chalk = require('chalk');
4 | var mkdirp = require('mkdirp');
5 | var inquirer = require('inquirer');
6 |
7 | module.exports = generators.Base.extend({
8 | // The name constructor is important here
9 | constructor: function () {
10 | // Calling the super constructor is important so our generator is correctly set up
11 | generators.Base.apply(this, arguments);
12 |
13 | // Next, add your custom code
14 | this.option('name', {
15 | type: String,
16 | desc: 'Title of the Office Add-in',
17 | required: false
18 | });
19 |
20 | this.option('site', {
21 | type: String,
22 | desc: 'SharePoint site URL',
23 | required: true
24 | });
25 |
26 | this.option('username', {
27 | type: String,
28 | desc: 'Username',
29 | required: true
30 | });
31 |
32 | this.option('password', {
33 | type: String,
34 | desc: 'Password',
35 | required: true
36 | });
37 |
38 | this.option('skipInstall', {
39 | type: Boolean,
40 | required: false,
41 | defaults: false,
42 | desc: 'Skip running NPM package manager at the end.'
43 | });
44 |
45 | this.option('download', {
46 | type: Boolean,
47 | desc: 'Download files after install?',
48 | required: true
49 | });
50 |
51 | this.genConfig = {};
52 | },
53 |
54 | prompting: {
55 | askFor: function () {
56 | var done = this.async();
57 |
58 | var prompts = [
59 | {
60 | name: 'username',
61 | message: 'Username:',
62 | default: null,
63 | when: this.options.username === undefined,
64 | type: 'input'
65 | },
66 | {
67 | name: 'password',
68 | message: 'Password:',
69 | default: null,
70 | when: this.options.password === undefined,
71 | type: 'password'
72 | },
73 | {
74 | name: 'download',
75 | message: 'Download files after installation?',
76 | default: true,
77 | type: 'confirm'
78 | }
79 | ];
80 |
81 | inquirer.prompt(prompts).then(function(responses) {
82 | this.genConfig = Object.assign({}, this.genConfig, this.options, responses);
83 | done();
84 | }.bind(this));
85 | }
86 | },
87 |
88 | writing: {
89 | app: function() {
90 | var done = this.async();
91 |
92 | // create common assets
93 | this.fs.copyTpl(this.templatePath('_gulpfile.js'),
94 | this.destinationPath('gulpfile.js'));
95 | this.fs.copyTpl(this.templatePath('_package.json'),
96 | this.destinationPath('package.json'));
97 | this.fs.copyTpl(this.templatePath('settings.js'),
98 | this.destinationPath('settings.js'));
99 |
100 | // Create and update the config file with the required properties
101 | var pathToConfigJson = this.templatePath('../../app/templates/config.json');
102 | if (this.fs.exists(pathToConfigJson)) {
103 | // Load config.json file
104 | var configJson = this.fs.readJSON(pathToConfigJson, 'utf8');
105 |
106 | // Set the required properties
107 | if (!configJson['username']) {
108 | configJson['username'] = this.genConfig.username;
109 | }
110 | if (!configJson['password']) {
111 | configJson['password'] = this.genConfig.password;
112 | }
113 | if (!configJson['site']) {
114 | configJson['site'] = this.genConfig.site;
115 | }
116 | if (!configJson['skipInstall']) {
117 | configJson['skipInstall'] = this.genConfig.skipInstall;
118 | }
119 | if (!configJson['location']) {
120 | configJson['location'] = "_catalogs/masterpage/" + this.genConfig.projectInternalName;
121 | }
122 |
123 | // Overwrite the existing config.json
124 | this.log(chalk.green('Adding your configuration to the config.json file'));
125 | this.fs.writeJSON(this.destinationPath('config.json'), configJson);
126 | }
127 |
128 | done();
129 | }
130 | },
131 |
132 | install: function() {
133 | // Run npm installer?
134 | if (!this.genConfig['skipInstall']) {
135 | this.npmInstall("", () => {
136 |
137 | // Run gulp download
138 | if (this.genConfig['download']) {
139 | this.spawnCommand('gulp', ['download']);
140 | }
141 | })
142 | } else {
143 | // Run gulp download
144 | if (this.genConfig['download']) {
145 | this.spawnCommand('gulp', ['download']);
146 | }
147 | }
148 | }
149 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Yeoman Display Template Generator
2 | Yeoman generator that gives you a kick start for building Search Display Templates.
3 | [](https://nodei.co/npm/generator-displaytemplates/)
4 |
5 | # Installation
6 | Run the following command to install the required packages and Yeoman display template generator:
7 |
8 | ```$ npm install -g gulp yo generator-displaytemplates```
9 |
10 | # Update
11 | Execute the following command to update the generator to the latest version:
12 |
13 | ```$ npm update -g generator-displaytemplates```
14 |
15 | # Usage
16 | Once you installed the display templates generator, you can execute this command:
17 |
18 | ```$ yo displaytemplates```
19 |
20 | 
21 |
22 | The generator asks you for the following things:
23 | - **Project name** (default: Search Display Templates)
24 | - **Site URL**
25 | - **Sample files** (default: true): creates sample display templates in a sample folder.
26 | - **Skip install** (default: false): if you set this to true, the ``npm install`` command will be skipped. Be aware, if you do this, it will not install the dependencies.
27 | - **Upload files via SharePoint add-in or client credentials** (choice)
28 | - SharePoint add-in: this option uses the [gulp-spsync](https://github.com/wictorwilen/gulp-spsync) plugin
29 | - client credentials: this option uses the [gulp-spsync-creds](https://github.com/estruyf/gulp-spsync-creds) plugin
30 |
31 | Depending on the type of upload mechanism you choose, the next questions will vary.
32 |
33 | **SharePoint add-in questions**
34 | - **Client ID:** specify the add-in client ID
35 | - **Client Secret**: specify the add-in secret
36 |
37 | **Client credentials questions**
38 | - **Username**
39 | - **Password**
40 | - **Download files after installation?** (default: true): automatically downloads the files from SharePoint "_catalogs/masterpage/your-project-name" after the generator ran.
41 |
42 | # Configuration
43 | The generator has two sub-generators:
44 | 1. SharePoint add-in generator
45 | 1. Client credentials generator
46 |
47 | The reason why is that the SharePoint add-in generator makes use of a Gulp plugin that only works for SharePoint Online sites. The client credentials generator will work both on for SharePoint Online and on-premises.
48 |
49 | **SharePoint add-in generator**
50 |
51 | The generator for the SharePoint add-in approach makes use of the **gulp-spsync** plugin created by Wictor Wilen. Go to the [gulp-spsync](https://github.com/wictorwilen/gulp-spsync) repo to check out the configuration process that you have to do on your SharePoint Online site.
52 |
53 | This generator requires the following things in order to run your gulp tasks afterwards:
54 | - **client_id**: this is the ID of your SharePoint add-in;
55 | - **client_secret**: this is the secret of your SharePoint add-in;
56 | - **site**: this is the site URL where your configured the SharePoint add-in.
57 |
58 | **Client credentials generator**
59 |
60 | The client credentials generator makes use of the **gulp-spsync-creds** plugin. Go to the [gulp-spsync-creds](https://github.com/estruyf/gulp-spsync-creds) repo for more information about this plugin.
61 |
62 | This generator requires the following things in order to run your gulp tasks afterwards:
63 | - **Username**: this is the username for accessing your site;
64 | - **Password**: this is the password for the given username;
65 | - **site**: this is the site URL to where you want to upload the files.
66 | - **startFolder**: this is only required for downloading files.
67 |
68 | # Development process
69 | Once you completed all previous steps, you can start your development process.
70 |
71 | ## Display template samples
72 | In the directory you will find a **sample** folder. This contains the following files which can be used to create new templates:
73 | - **control_starter.html**: this is a starter control display template;
74 | - **item_starter.html**: this is a starter item display template;
75 | - **control_minimal.js**: this is a starter control JavaScript display template (this template does not require a HTML file);
76 | - **item_minimal.js**: this is a starter item JavaScript display template (this template does not require a HTML file);
77 |
78 | ## Display template creation
79 | Create your display templates in the following folder:
80 | ```
81 | .
82 | ├── src/
83 | │ └── _catalogs/
84 | │ └── masterpage/
85 | │ └── (default: search-display-templates)/
86 | │ └── Create all your files in this folder
87 | ```
88 |
89 | ## Upload, download, watch, publish
90 | The following Gulp tasks are available:
91 |
92 | **gulp**: this task uploads all the display templates to the masterpage gallery
93 | ```
94 | $ gulp
95 | ```
96 |
97 | **gulp set-metadata**: uploads all the files and sets the metadata for the files. Metadata has to be specified in the config.json file and contains sample data.
98 | ```
99 | $ gulp set-metadata
100 | ```
101 |
102 | **gulp set-metadata**: uploads all the files and sets the metadata for the files. Metadata has to be specified in the config.json file and contains sample data.
103 | ```
104 | $ gulp set-metadata
105 | ```
106 |
107 | **gulp publish**: uploads all the files, sets metadata and publishes each of the files.
108 | ```
109 | $ gulp publish
110 | ```
111 |
112 | **gulp watch**: watches for file changes, once a change happens, the file will get uploaded.
113 | ```
114 | $ gulp watch
115 | ```
116 |
117 | **gulp watch-metadata**: watches for file changes, once a change happens, the file will get uploaded and metadata will get set.
118 | ```
119 | $ gulp watch-metadata
120 | ```
121 |
122 | **gulp download**: download all the files of the folder location "_catalogs/masterpage/your-project-name".
123 | ```
124 | $ gulp publish
125 | ```
--------------------------------------------------------------------------------
/generators/app/index.js:
--------------------------------------------------------------------------------
1 | var generators = require('yeoman-generator');
2 | var yosay = require('yosay');
3 | var chalk = require('chalk');
4 | var mkdirp = require('mkdirp');
5 | var inquirer = require('inquirer');
6 | var updateNotifier = require('update-notifier');
7 |
8 | var pkg = require('../../package.json');
9 |
10 | module.exports = generators.Base.extend({
11 | // The name constructor is important here
12 | constructor: function () {
13 | // Calling the super constructor is important so our generator is correctly set up
14 | generators.Base.apply(this, arguments);
15 |
16 | // Next, add your custom code
17 | this.option('name', {
18 | type: String,
19 | desc: 'Title of the Office Add-in',
20 | required: false
21 | });
22 |
23 | this.option('client_id', {
24 | type: String,
25 | desc: 'SharePoint add-in client id',
26 | required: true
27 | });
28 |
29 | this.option('client_secret', {
30 | type: String,
31 | desc: 'SharePoint add-in secret',
32 | required: true
33 | });
34 |
35 | this.option('site', {
36 | type: String,
37 | desc: 'SharePoint site URL',
38 | required: true
39 | });
40 |
41 | this.option('skipInstall', {
42 | type: Boolean,
43 | required: false,
44 | defaults: false,
45 | desc: 'Skip running NPM package manager at the end.'
46 | });
47 | },
48 |
49 | initializing: function() {
50 | var notifier = updateNotifier({pkg});
51 | notifier.notify({defer: false});
52 |
53 | this.log(yosay('Welcome to the\n' +
54 | chalk.yellow('Display Templates') +
55 | ' generator version: ' + pkg.version + '.' +
56 | ' Let\'s create a new project.'));
57 |
58 | // generator configuration
59 | this.genConfig = {};
60 | },
61 |
62 | prompting: {
63 | askFor: function () {
64 | var done = this.async();
65 |
66 | var prompts = [{
67 | name: 'name',
68 | message: 'Project name',
69 | default: 'Search Display Templates',
70 | when: this.options.name === undefined,
71 | type: 'input'
72 | },
73 | {
74 | name: 'site',
75 | message: 'What is the SharePoint site URL?',
76 | default: null,
77 | when: this.options.site === undefined,
78 | type: 'input'
79 | },
80 | {
81 | name: 'sample',
82 | message: 'Do you want the sample files?',
83 | default: true,
84 | type: 'confirm'
85 | },
86 | {
87 | name: 'skipInstall',
88 | message: 'Skip NPM install at the end?',
89 | default: false,
90 | when: this.options.name === undefined,
91 | type: 'confirm'
92 | },
93 | {
94 | name: 'plugin',
95 | message: 'Upload files via SharePoint add-in or client credentials?',
96 | type: 'list',
97 | default: 'spsync-creds',
98 | choices: [
99 | {
100 | name: 'SharePoint Add-in (SP Online only)',
101 | value: 'spsync'
102 | },
103 | {
104 | name: 'Client credentials',
105 | value: 'spsync-creds'
106 | }
107 | ]
108 | }
109 | ];
110 |
111 | inquirer.prompt(prompts).then(function(responses) {
112 | this.genConfig = Object.assign({}, this.genConfig, this.options, responses);
113 |
114 | done();
115 | }.bind(this));
116 | }
117 | },
118 |
119 | configuring: function(){
120 | // take name submitted and strip everything out non-alphanumeric or space
121 | var projectName = this.genConfig.name;
122 | projectName = projectName.replace(/[^\w\s\-]/g, '');
123 | projectName = projectName.replace(/\s{2,}/g, ' ');
124 | projectName = projectName.trim();
125 |
126 | // add the result of the question to the generator configuration object
127 | this.genConfig.projectInternalName = projectName.toLowerCase().replace(/ /g, '-');
128 | this.genConfig.projectDisplayName = projectName;
129 | this.genConfig.rootPath = this.genConfig['root-path'];
130 | },
131 |
132 | writing: {
133 | app: function() {
134 | // helper function to build path to the file off root path
135 | this._parseTargetPath = function(file){
136 | return path.join(this.genConfig['root-path'], file);
137 | };
138 |
139 | var done = this.async();
140 |
141 | switch (this.genConfig.plugin) {
142 | case 'spsync':
143 | this.composeWith('displaytemplates:spsync', {
144 | options: {
145 | name: this.genConfig.name,
146 | projectInternalName: this.genConfig.projectInternalName,
147 | site: this.genConfig.site,
148 | skipInstall: this.genConfig.skipInstall
149 | }
150 | }, {
151 | local: require.resolve('../spsync')
152 | });
153 | break;
154 | case 'spsync-creds':
155 | this.composeWith('displaytemplates:spsync-creds', {
156 | options: {
157 | name: this.genConfig.name,
158 | projectInternalName: this.genConfig.projectInternalName,
159 | site: this.genConfig.site,
160 | skipInstall: this.genConfig.skipInstall
161 | }
162 | }, {
163 | local: require.resolve('../spsync-creds')
164 | });
165 | break;
166 | }
167 |
168 | if (this.genConfig.sample) {
169 | // Create sample files
170 | this.fs.copyTpl(this.templatePath('sample/control_minimal.js'),
171 | this.destinationPath('sample/control_minimal.js'));
172 | this.fs.copyTpl(this.templatePath('sample/item_minimal.js'),
173 | this.destinationPath('sample/item_minimal.js'));
174 | this.fs.copyTpl(this.templatePath('sample/control_starter.html'),
175 | this.destinationPath('sample/control_starter.html'));
176 | this.fs.copyTpl(this.templatePath('sample/item_starter.html'),
177 | this.destinationPath('sample/item_starter.html'));
178 | }
179 |
180 | // Create folder structure
181 | mkdirp(this.destinationPath() + '/src/_catalogs/masterpage/' + this.genConfig.projectInternalName, function (err) {
182 | if (!err) {
183 | // Folder created
184 | }
185 | });
186 |
187 | done();
188 | }
189 | },
190 |
191 | install: function() {
192 | // Nothing to do
193 | }
194 | });
--------------------------------------------------------------------------------