Compare commits

...

11 Commits

Author SHA1 Message Date
6c858da354 Updated Readme again! 2022-01-04 11:03:19 -07:00
bb01b190df Updated Readme with all topics covered in course 2022-01-04 10:59:23 -07:00
0636939cc7 Beefed up .gitignore
Added sample files folder
Working on pants import job example
Added instructions on how to use node, tested with existing project
2021-12-22 17:33:29 -07:00
e28f44fe57 restructured learning cartridge so it is correctly registered with prophet 2021-12-22 17:06:51 -07:00
Isaac Vallee
d162ce8ed6 Update folder structure & remove unecessary redundant directory 2021-12-22 13:39:34 -08:00
Isaac Vallee
ade208263e Merge branch 'master' of http://24.121.68.9:3333/max/LearningSalesForceCommerceCloud 2021-12-22 10:32:44 -08:00
0825ca85a7 Added Ocapi settings example json 2021-12-22 11:21:12 -07:00
Isaac Vallee
91355f8903 Properly extends ContactUs controller 2021-12-22 10:20:13 -08:00
Max Gialanella
21978218b6 Merge branch 'master' of http://24.121.68.9:3333/max/LearningSalesForceCommerceCloud 2021-12-22 11:10:35 -07:00
Max Gialanella
3f354195f2 ticket #3 - Added learning cartridge with all complete examples 2021-12-22 11:09:55 -07:00
Isaac Vallee
8a0742d9a8 change append to prepend 2021-12-22 10:05:38 -08:00
45 changed files with 1284 additions and 7 deletions

42
.gitignore vendored
View File

@@ -2,4 +2,44 @@ node_modules/
static/
dw.json
.DS_Store
.vscode
.vscode
cartridges/app_storefront_base/cartridge/static/default/css/
cartridges/app_storefront_base/cartridge/static/default/js/
cartridges/app_storefront_base/cartridge/static/default/fonts/
cartridges/app_storefront_base/cartridge/static/fr_FR/css/
cartridges/app_storefront_base/cartridge/static/fr_FR/js/
cartridges/app_storefront_base/cartridge/static/fr_FR/fonts/
cartridges/app_storefront_base/cartridge/static/it_IT/css/
cartridges/app_storefront_base/cartridge/static/it_IT/js/
cartridges/app_storefront_base/cartridge/static/it_IT/fonts/
cartridges/app_storefront_base/cartridge/static/ja_JP/css/
cartridges/app_storefront_base/cartridge/static/ja_JP/js/
cartridges/app_storefront_base/cartridge/static/ja_JP/fonts/
cartridges/app_storefront_base/cartridge/static/zh_CN/css/
cartridges/app_storefront_base/cartridge/static/zh_CN/js/
cartridges/app_storefront_base/cartridge/static/zh_CN/fonts/
cartridges/app_storefront_base/cartridge/static/en_GB/css/
cartridges/app_storefront_base/cartridge/static/en_GB/js/
cartridges/app_storefront_base/cartridge/static/en_GB/fonts/
coverage/
npm-debug.log
cartridges.zip
.idea/
sitegenesisdata/
mobilefirstdata/
storefrontdata/
demo_data_sfra/
demo_data_sfra.zip
test/appium/webdriver/config.json
.history
*.iml
.idea
test/acceptance/report
test/integration/config.json

View File

@@ -0,0 +1,32 @@
{
"_v": "21.9a",
"clients": [
{
"allowed_origins": [
"http://www.sitegenesis.com",
"https://secure.sitegenesis.com"
],
"client_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"resources": [
{
"resource_id": "/product_search",
"methods": [
"get"
],
"read_attributes": "(**)",
"write_attributes": "(**)",
"cache_time": 900
},
{
"resource_id": "/products/*",
"methods": [
"get"
],
"read_attributes": "(**)",
"write_attributes": "(**)",
"cache_time": 900
}
]
}
]
}

312
Example Files/pants-import.xml Executable file
View File

@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.demandware.com/xml/impex/catalog/2006-10-31" catalog-id="apparel-m-catalog">
<header>
<image-settings>
<internal-location base-path="/images"/>
<view-types>
<view-type>swatch</view-type>
<view-type>large</view-type>
<view-type>medium</view-type>
<view-type>small</view-type>
</view-types>
<variation-attribute-id>color</variation-attribute-id>
<alt-pattern>${productname}, ${variationvalue}, ${viewtype}</alt-pattern>
<title-pattern>${productname}, ${variationvalue}</title-pattern>
</image-settings>
</header>
<product product-id="blue-pants-1">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1.0</min-order-quantity>
<step-quantity>1.0</step-quantity>
<display-name xml:lang="x-default">Blue Pants</display-name>
<long-description xml:lang="x-default">Blue pants made of denim. Also known as Jeans.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">blue</custom-attribute>
<custom-attribute attribute-id="refinementColor">blue</custom-attribute>
<custom-attribute attribute-id="size">S</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="red-pants-1">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Red Pants</display-name>
<short-description xml:lang="x-default">Matador style pants</short-description>
<long-description xml:lang="x-default">Perfect gift for your bullfighter friend.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">red</custom-attribute>
<custom-attribute attribute-id="refinementColor">red</custom-attribute>
<custom-attribute attribute-id="size">S</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="blue-pants-2">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Blue Pants</display-name>
<long-description xml:lang="x-default">Specifically tailored to camouflage in a pool.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">blue</custom-attribute>
<custom-attribute attribute-id="refinementColor">blue</custom-attribute>
<custom-attribute attribute-id="size">M</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="white-pants-1">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">White Pants</display-name>
<short-description xml:lang="x-default">The cleanest pants you have ever seen.</short-description>
<long-description xml:lang="x-default">Do not wear these pants while eating, walking or cleaning as they may get dirty.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">white</custom-attribute>
<custom-attribute attribute-id="refinementColor">white</custom-attribute>
<custom-attribute attribute-id="size">S</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="white-pants-2">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">White Pants</display-name>
<short-description xml:lang="x-default">The sheen of the cosmos on your legs.</short-description>
<long-description xml:lang="x-default">A clean, blinding white. Color modeld after the sun. The very star that sustains this planet.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">white</custom-attribute>
<custom-attribute attribute-id="refinementColor">white</custom-attribute>
<custom-attribute attribute-id="size">M</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="red-pants-2">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Red Pants</display-name>
<short-description xml:lang="x-default">Christmas Red</short-description>
<long-description xml:lang="x-default">Wear these pants with a green shirt and everyone will think you are an elf. </long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">red</custom-attribute>
<custom-attribute attribute-id="refinementColor">red</custom-attribute>
<custom-attribute attribute-id="size">M</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="red-pants-3">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Red Pants</display-name>
<short-description xml:lang="x-default">The color of fire and rage.</short-description>
<long-description xml:lang="x-default">Red Rage Pants. Perfert for fits of rage.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">red</custom-attribute>
<custom-attribute attribute-id="refinementColor">red</custom-attribute>
<custom-attribute attribute-id="size">L</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="white-pants-3">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1.0</min-order-quantity>
<step-quantity>1.0</step-quantity>
<display-name xml:lang="x-default">White Pants</display-name>
<short-description xml:lang="x-default">All the colors combined.</short-description>
<long-description xml:lang="x-default">Mix all the colors together and you get the color of these pants.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">white</custom-attribute>
<custom-attribute attribute-id="refinementColor">white</custom-attribute>
<custom-attribute attribute-id="size">L</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
<product product-id="blue-pants-3">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Blue Pants</display-name>
<long-description xml:lang="x-default">They look like whale skin. Don't worry. They are vegan.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<available-flag>false</available-flag>
<searchable-flag>true</searchable-flag>
<searchable-if-unavailable-flag>false</searchable-if-unavailable-flag>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="color">blue</custom-attribute>
<custom-attribute attribute-id="refinementColor">blue</custom-attribute>
<custom-attribute attribute-id="size">L</custom-attribute>
</custom-attributes>
<pinterest-enabled-flag>false</pinterest-enabled-flag>
<facebook-enabled-flag>false</facebook-enabled-flag>
<store-attributes>
<force-price-flag>false</force-price-flag>
<non-inventory-flag>false</non-inventory-flag>
<non-revenue-flag>false</non-revenue-flag>
<non-discountable-flag>false</non-discountable-flag>
</store-attributes>
</product>
</catalog>

View File

@@ -1,9 +1,15 @@
'use strict';
var base = module.superModule;
var server = require('server');
server.extend(base);
// First, let's extend the ContactUs-Landing route
// to add recaptcha data to the template view
server.append('Landing', function(req, res, next) {
res.setViewData({
// Recaptcha API script URL
recaptchaUrl: 'https://www.google.com/recaptcha/api.js',
siteKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI' // Test Site Key from Google
});
@@ -11,21 +17,26 @@ server.append('Landing', function(req, res, next) {
next();
});
server.append('Subscribe', function(req, res, next) {
// Now we need to extend ContactUs-Subscribe, to add our logic
// that invokes the recaptcha service
// We prepend here to ensure that an invalid response is caught before the email is sent
server.prepend('Subscribe', function(req, res, next) {
var recaptchaService = require('*/cartridge/scripts/services/recaptcha');
// Token automatically added to request by recaptcha
var token = req.form['g-recaptcha-response'];
// Add required parameters for validation call
var params = {
// This is a test secret from Google
// In practice, do not hardcode this. It's better to store
// In a custom preference or service credential Configuration
secret: '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe', // Test secret key from Google
// In practice, do not hardcode this.
// It's better to store in a custom preference or service credential configuration
secret: '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe',
response: token
};
// Calls recaptcha service
// Returns a Service class instance
// (start with a hardcoded fail)
var validationResult = recaptchaService.call(params);
// Actual response from Google

View File

@@ -1,2 +1,73 @@
So you want to learn SFCC?
You have come to the right place
# Sales Force Commerce Cloud Training Course
Course Material can be found here [Confluence Page](https://confluence.ontrq.com/display/SA/SFCC+Engineer+Training)
## Topics Covered
1. Material in this guide
1. Introductory Material
1. Beginner Gochas
1. Get an On-Demand Sandbox setup
1. Setting up your Sandbox
1. Import a Demo Site
1. Configure Site Settings for development
1. Overview: Set up VSCode and Local environment
1. Download and Install Visual Studio Code (VS Code)
1. Install Prophet Extension
1. Download or Clone the SFRA storefront code from Github
1. Generate access keys in business manager to upload to allow code upload
1. Browse around the prophet debugger extension tab
1. Compile CSS and JS for storefront website
1. Setup Prophet Code Debugger and Breakpoints
1. Storefront Debug Tools
1. Cartridges and Activating Code Versions
1. Create a New Code Version
1. Create your first cartridge
1. Modify the Cartridge Path
1. Plugin Cartridges
1. Create your First Controller
1. Navigate to a new Route
1. Create another Controller
1. Extend a Controller
1. Customizing Existing Pages - Home Page
1. Middleware Scripts and Chaining
1. Middleware Chaining
1. Pages and Templates - Demo Page
1. Extending a Model
1. Using a decorator to extend a model
1. ISML Templates
1. ISML Template Includes
1. ISML Local Includes
1. ISML Loops
1. ISML Remote Include
1. ISML Debug and extending existing templates
1. Localization using resource bundles
1. Enable Localizations in Business Manager
1. Introduction to Content Slots
1. Create a content slot in a template
1. Setup a Content slot in business manager
1. The Page Designer
1. Create a new page in page designer
1. Layout a new page in page designer
1. Preview your new page, using the page designer previewer
1. Create a route/controller to render your new page designer page
1. Create a custom component for page designer
1. Use and Preview the Feedback Custom Page Designer Component
1. Page Caching
1. Define a Custom Object Type
1. Create a custom attribute group
1. Forms Framework
1. Transactions in a more depth
1. Enable Custom Logging
1. Integrate custom logging
1. View Custom Logs
1. Extend a system object and capture custom data
1. Notification Email - Using a custom hook
1. Making API calls with OCAPI - Open Commerce API
1. Uploading a Product Catalog XML file
1. Create a Job to Import pants.xml
1. Tracking performance of storefront
1. The Pipeline Profiler
1. The Code Profiler
1. Useful Global Variables
1. Acronyms
1. Reference

View File

@@ -0,0 +1,17 @@
<?xml version='1.0' encoding='UTF-8'?>
<projectDescription>
<name>the_learning_cartridge</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.demandware.studio.core.beehiveElementBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.demandware.studio.core.beehiveNature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,11 @@
{
'ecmaVersion': 5,
'plugins': {
'guess-types': {
},
'outline': {
},
'demandware': {
}
}
}'

View File

@@ -0,0 +1,57 @@
'use strict';
var server = require('server');
var cyborgForm = server.forms.getForm('cyborgregistrationform');
var Logger = require('dw/system/Logger');
var log = Logger.getLogger('DevTest','Cyborg');
server.get('Start', function (req, res, next) {
cyborgForm.clear();
log.info('Form loaded.');
res.render('cyborgTracker/registrationform', {
cyborgForm: cyborgForm,
disabled: false
});
next();
});
server.post('HandleForm', function (req, res, next) {
var storageService = require('~/cartridge/scripts/storageService');
var Transaction = require('dw/system/Transaction');
var message = '';
log.info('Cyborg form handler loaded.');
Transaction.begin();
try {
var co = storageService.storeCyborgForm(cyborgForm);
Transaction.commit();
message = 'Form Saved';
log.info('Cyborg form saved');
}
catch (e){
Transaction.rollback();
message = 'Failed to save';
log.warn('Cyborg form failed to save ' + e);
}
res.render('cyborgTracker/registrationform', {
cyborgForm: cyborgForm,
message: message,
disabled: true
});
next();
});
module.exports = server.exports();

View File

@@ -0,0 +1,43 @@
'use strict';
var server = require('server');
var cache = require('*/cartridge/scripts/middleware/cache');
var ProductFactory = require('*/cartridge/scripts/factories/product');
var StoreMgr = require('dw/catalog/StoreMgr');
var Logger = require('dw/system/Logger');
var log = Logger.getLogger('DevTest','Cyborg');
server.get('Start',
cache.applyShortPromotionSensitiveCache,
function (req, res, next) {
var params = {'pid':'sony-kdl-55xbr8M'};
var productModel = ProductFactory.get(params);
var storeModel = StoreMgr.getStore('store1');
var viewData = {
'productModel': productModel,
'storeModel': storeModel
};
log.debug('Loaded demo page');
res.render('demo', viewData);
next();
});
server.get('Designer', function (req, res, next) {
let PageMgr = require('dw/experience/PageMgr');
let page = PageMgr.getPage('testpage');
log.warn('Loaded page designer page');
res.print(PageMgr.renderPage(page.ID, 'testpage'));
next();
});
module.exports = server.exports();

View File

@@ -0,0 +1,22 @@
'use strict';
var server = require('server');
server.get('Start', function (req, res, next) {
res.print('Greetings Planet');
next();
});
server.get('Showme', function (req, res, next) {
// Use template hello.isml
// Pass data to pdict using name param1
res.render('hello', { 'param1':'This will render.' });
next();
});
module.exports = server.exports();

View File

@@ -0,0 +1,44 @@
'use strict';
var server = require('server');
//Use super module to extend existing home route
server.extend(module.superModule);
//import userLoggedIn middleware
var userLoggedIn = require('*/cartridge/scripts/middleware/userLoggedIn');
//Use prepend to check for logged in user
server.prepend('Show', userLoggedIn.validateLoggedIn, function (req, res, next) {
var viewData = res.getViewData();
viewData.detailText = 'Please log in'; //One method to set view data
if (req.currentCustomer.profile) {
viewData.detailText = 'Welcome ' + customer.getProfile().getFirstName();
}
next();
});
//Use append to check for promotion
server.append('Show', function (req, res, next) {
var promoText = 'There are no Promotions at this time';
//read query string to check for fromotions
if (req.querystring.promo == 1) {
promoText = 'All Electronics are 98% off! Practically free!';
}
if (req.querystring.promo == 2) {
promoText = 'Overnight shipping is free!';
}
res.setViewData(
{ promoText: promoText } //Another way to set view data
);
next();
});
module.exports = server.exports();

View File

@@ -0,0 +1,79 @@
'use strict';
var server = require('server');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
var userLoggedIn = require('*/cartridge/scripts/middleware/userLoggedIn');
var consentTracking = require('*/cartridge/scripts/middleware/consentTracking');
var Logger = require('dw/system/Logger');
var log = Logger.getLogger('DevTest','Notifications');
server.get('Start',
server.middleware.https,
userLoggedIn.validateLoggedIn,
consentTracking.consent,
csrfProtection.generateToken,
function (req, res, next) {
var notificationsForm = server.forms.getForm('notifications');
notificationsForm.clear();
if(customer.profile.custom.notificationSales){
notificationsForm.notificationSales.checked = 'checked';
}
if(customer.profile.custom.notificationNew){
notificationsForm.notificationNew.checked = 'checked';
}
if(customer.profile.custom.notificationStock){
notificationsForm.notificationStock.checked = 'checked';
}
res.render('notificationsform', {
notificationsForm: notificationsForm
});
log.debug('Rendered Notifications Form');
next();
});
server.post('HandleForm',
server.middleware.https,
csrfProtection.validateRequest,
function (req, res, next) {
var notificationsForm = server.forms.getForm('notifications');
var URLUtils = require('dw/web/URLUtils');
var Transaction = require('dw/system/Transaction');
Transaction.begin();
try {
customer.profile.custom.notificationSales = notificationsForm.notificationSales.value
customer.profile.custom.notificationNew = notificationsForm.notificationNew.value
customer.profile.custom.notificationStock = notificationsForm.notificationStock.value
Transaction.commit();
log.error('Saved notifications');
var HookMgr = require('dw/system/HookMgr');
var userEmail = customer.getProfile().getEmail();
HookMgr.callHook('app.notification.email', 'sendNotificationsChange', userEmail);
} catch (error) {
Transaction.rollback();
log.error(error);
}
// res.redirect( URLUtils.url('Notifications') ); //For testing
res.redirect( URLUtils.url('Account-Show') );
next();
})
module.exports = server.exports();

View File

@@ -0,0 +1,19 @@
'use strict';
var server = require('server');
//Basket Manager will get all the users basket information
var BasketMgr = require('dw/order/BasketMgr');
server.get('List', function (req, res, next) {
var viewData = {
'basket': BasketMgr.getCurrentOrNewBasket()
};
res.render('simplebasket', viewData);
next();
});
module.exports = server.exports();

View File

@@ -0,0 +1,22 @@
'use strict';
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
/**
* Render logic for storefront.imageAndText component.
* @param {dw.experience.ComponentScriptContext} context The Component script context object.
* @param {dw.util.Map} [modelIn] Additional model values created by another cartridge.
*
* @returns {string} The markup to be displayed
*/
module.exports.render = function (context, modelIn) {
var model = modelIn || new HashMap();
var content = context.content;
model.title = content.title ? content.title : null;
model.comment = content.comment ? content.comment : null;
model.name = content.name ? content.name : null;
return new Template('experience/components/commerce_assets/feedback').render(model).text;
};

View File

@@ -0,0 +1,36 @@
{
"name": "Feedback",
"description": "Hardcoded customer feedback",
"group": "Custom",
"attribute_definition_groups": [
{
"id": "feedback_content",
"name": "Feedback Content",
"description": "Enter feedback below, all fields required",
"attribute_definitions": [
{
"id": "title",
"name": "Title",
"description": "title of feedback",
"type": "string",
"required": true
},
{
"id": "comment",
"name": "Comment",
"description": "Text body of feedback",
"type": "markup",
"required": true
},
{
"id": "name",
"name": "Username",
"description": "User who submitted this feebback",
"type": "string",
"required": true
}
]
}
],
"region_definitions": []
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<form>
<field formid="humanname" label="Human Name" type="string" mandatory="true" max-length="50"/>
<field formid="cyborgname" label="Cyborg Name" type="string" mandatory="true" max-length="50"/>
<field formid="email" label="label.input.email.profile" type="string" mandatory="true"
regexp="^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,6}$"
parse-error="error.message.parse.email.profile.form"
value-error="error.message.parse.email.profile.form"
max-length="50"/>
<field formid="mostlyhuman" label="Mostly Human" type="boolean" />
<action formid="subscribe" valid-form="true"/>
</form>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<form>
<field formid="notificationSales" label="label.notification.sales" type="boolean" />
<field formid="notificationNew" label="label.notification.new" type="boolean" />
<field formid="notificationStock" label="label.notification.stock" type="boolean" />
<action formid="apply" label="button.notification.submit" valid-form="true"/>
</form>

View File

@@ -0,0 +1,9 @@
'use strict';
module.exports = function (object, productApi) {
var inventoryRecord = productApi.availabilityModel.inventoryRecord;
Object.defineProperty(object, 'stockInformation', {
enumerable: true,
value: inventoryRecord==null ? 0 :parseInt(inventoryRecord.ATS,10)
});
};

View File

@@ -0,0 +1,23 @@
'use strict';
var base = module.superModule;
//Load our stock information decorator
var stockInformation = require('*/cartridge/models/product/decorators/stockInformation');
/**
* Decorate product with product line item information
* @param {Object} product - Product Model to be decorated
* @param {dw.catalog.Product} apiProduct - Product information returned by the script API
* @param {Object} options - Options passed in from the factory
* @returns {Object} - Decorated product model
*/
function fullProduct(product, apiProduct, options) {
//Call base, pass all the same paramters
base.call(this, product, apiProduct, options);
//use decorator to lead invintory from product api
stockInformation(product,apiProduct);
return product;
}
module.exports = fullProduct;

View File

@@ -0,0 +1,12 @@
'use strict';
var base = module.superModule;
function store(storeObject) {
base.call(this, storeObject);
this.email = storeObject.email;
}
store.prototype = Object.create(base.prototype);
module.exports = store;

View File

@@ -0,0 +1,23 @@
'use strict';
function sendNotificationsChange(emailAddress) {
var Mail = require('dw/net/Mail');
var Site = require('dw/system/Site');
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var context = new HashMap();
var email = new Mail();
var template = new Template('notificationchangeemail');
email.addTo(emailAddress);
email.setFrom(Site.current.getCustomPreferenceValue('customerServiceEmail') || 'no-reply@salesforce.com');
email.setSubject('Notification Settings Changed');
email.setContent(template.render(context).text, 'text/html', 'UTF-8');
email.send();
}
module.exports = {
sendNotificationsChange: sendNotificationsChange
};

View File

@@ -0,0 +1,17 @@
'use strict';
var CustomObjectMgr = require('dw/object/CustomObjectMgr');
function storeCyborgForm(cyborgForm) {
var CustomObject = CustomObjectMgr.createCustomObject('Cyborgtracker', cyborgForm.email.value);
CustomObject.custom.cyborgname = cyborgForm.cyborgname.value;
CustomObject.custom.humanname = cyborgForm.humanname.value;
CustomObject.custom.email = cyborgForm.email.value;
CustomObject.custom.mostlyhuman = cyborgForm.mostlyhuman.value;
return CustomObject;
}
module.exports = {storeCyborgForm: storeCyborgForm};

View File

@@ -0,0 +1,26 @@
<isdecorate template="common/layout/page">
<isif condition="${pdict.reportingURLs && pdict.reportingURLs.length}">
<isinclude template="reporting/reportingUrls" />
</isif>
<isscript>
var assets = require('*/cartridge/scripts/assets.js');
assets.addCss('/css/account/dashboard.css');
</isscript>
<div class="hero slant-down account-image">
<h1 class="page-title">${Resource.msg('page.heading.dashboard','account',null)}</h1>
</div>
<div class="container">
<!---Breadcrumbs--->
<isinclude template="components/breadcrumbs/pageBreadcrumbs"/>
<iscomment> custom notifications form </iscomment>
<isinclude url="${ URLUtils.url('Notifications') }" />
<isinclude template="account/dashboardProfileCards"/>
</div>
</isdecorate>

View File

@@ -0,0 +1,74 @@
<isdecorate template="common/layout/page">
<isif condition="${pdict.message && pdict.message != ''}">
<div class="container mt-3 mb-3">
<h3 class="primary-text">${pdict.message}</h3>
</div>
</isif>
<div class="container mt-3 mb-3">
<form action="${URLUtils.url('Cyborg-HandleForm')}" method="POST">
<div class="form-row">
<iscomment> human name </iscomment>
<div class="col form-group">
<label for="humanname">
${ pdict.cyborgForm.humanname.label }
</label>
<input class="form-control" type="text" id="humanname"
<isprint value="${pdict.cyborgForm.humanname.attributes}" encoding="off" />
<isprint value="${pdict.disabled ? 'disabled':''}" encoding="off" />
/>
</div>
<iscomment> cyborg name </iscomment>
<div class="col form-group">
<label for="cyborgname">
${pdict.cyborgForm.cyborgname.label}
</label>
<input class="form-control" type="text" id="cyborgname"
<isprint value="${pdict.cyborgForm.cyborgname.attributes}" encoding="off" />
<isprint value="${pdict.disabled ? 'disabled':''}" encoding="off" />
/>
</div>
<iscomment> email </iscomment>
<div class="col form-group">
<label for="email">
${pdict.cyborgForm.email.label}
</label>
<input class="form-control" type="text" id="email"
<isprint value="${pdict.cyborgForm.email.attributes}" encoding="off" />
<isprint value="${pdict.disabled ? 'disabled':''}" encoding="off" />
/>
</div>
</div>
<iscomment> mostly human </iscomment>
<div class="form-row">
<div class="form-check form-check-inline">
<input type="checkbox" id="mostlyhuman"
<isprint value="${pdict.cyborgForm.mostlyhuman.attributes}" encoding="off" />
<isprint value="${pdict.disabled ? 'disabled':''}" encoding="off" />
/>
<label class="form-check-label" for="mostlyhuman">${pdict.cyborgForm.mostlyhuman.label}
</label>
</div>
</div>
<isif condition="${pdict.disabled}">
<a href="${ URLUtils.url('Cyborg') }">Back to Edit</a>
<iselse/>
<iscomment> submit button </iscomment>
<div class="form-row">
<button type="submit" class="btn btn-primary">
${Resource.msg('button.submit.promo.code','cart', null)}
</button>
</div>
</isif>
</form>
</div>
</isdecorate>

View File

@@ -0,0 +1,37 @@
<isdecorate template="common/layout/page">
<div class="container mt-5 mb-5">
<div class="card">
<div class="card-body">
<isslot id="demo-slot" description="Demo Slot" context="global" />
</div>
</div>
</div>
<div class="container mt-5 mb-5">
<h3 class="text-primary">
${ Resource.msg('label.title','demo', 'demo title') }
</h3>
<h1>
${pdict.storeModel.name}
</h1>
<h2>
${pdict.storeModel.email} - ${pdict.storeModel.postalCode}
</h2>
<h3 class="text-primary mt-5">
${ Resource.msg('label.featured','demo', 'demo featured') }
</h3>
<h1>
${pdict.productModel.productName}
</h1>
<h2>
${ Resource.msg('label.stock','demo', 'demo stock') }:
${pdict.productModel.stockInformation}
</h2>
</div>
<isinclude url="${URLUtils.url('SimpleBasket-List')}" />
<isinclude template="demoinclude" />
</isdecorate>

View File

@@ -0,0 +1,3 @@
<div class="container">
New Demos coming soon
</div>

View File

@@ -0,0 +1,13 @@
<div class="card" style="width: 25rem;">
<div class="card-body">
<h5 class="card-title">
<isprint value="${pdict.title}" />
</h5>
<p class="card-text">
<isprint value="${pdict.comment}" encoding="off" />
</p>
<h6 class="card-subtitle mb-2 text-muted">
- <isprint value="${pdict.name}" />
</h6>
</div>
</div>

View File

@@ -0,0 +1,5 @@
<html>
<body>
<h1>${pdict.param1}</h1>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<isdecorate template="common/layout/page">
<isscript>
var assets = require('*/cartridge/scripts/assets.js');
assets.addJs('/js/productTile.js');
assets.addCss('/css/homePage.css');
</isscript>
<div class="container mt-4 mb-4">
<h1>${pdict.detailText}</h1>
<h2 class="text-primary">${pdict.promoText}</h2>
</div>
<!-- +1.888.555.0199 --><!--This phone is a requirement to support existing Gomez monitor of SiteGenesis. Demadware customers can remove this.-->
<div class="home-main homepage">
<isslot id="home-main-m" description="Main home page slot." context="global" />
</div>
<div class="container home-categories homepage">
<div class="row home-main-categories no-gutters">
<isslot id="home-categories-m" description="Categories slots on the home page." context="global" />
</div>
</div>
<div class="container home-product-tiles homepage">
<div class="hp-product-grid" itemtype="http://schema.org/SomeProducts" itemid="#product">
<isslot id="home-products-m" description="Product tiles on the home page." context="global" />
</div>
</div>
<div class="homepage shop-the-style">
<isslot id="home-product-set-m" description="Link to a Product Set." context="global" />
</div>
<div class="home-email-signup">
<div class="container">
<form role="form">
<div class="row">
<div class="col-sm-5">
<div class="input-group">
<input type="text" class="form-control" name="hpEmailSignUp" placeholder="${Resource.msg('placeholdertext.form.emailsignup', 'homePage', null)}" aria-label="${Resource.msg('placeholdertext.form.emailsignup', 'homePage', null)}">
<span class="input-group-append">
<button type="submit" class="btn btn-primary subscribe-email" data-href="${URLUtils.url('EmailSubscribe-Subscribe')}">${Resource.msg('button.form.emailsignup', 'homePage', null)} </button>
</span>
</div>
</div>
<div class="col-sm-7 email-description">${Resource.msg('description.form.emailsignup', 'homePage', null)}</div>
</div>
</form>
</div>
</div>
</isdecorate>

View File

@@ -0,0 +1,3 @@
<p>
Your Notification settings have been changed.
</p>

View File

@@ -0,0 +1,57 @@
<div class="container mt-3 mb-3">
<h4 class="text-primary">${ Resource.msg('title.notifications','forms', null) }</h4>
<form action="${ URLUtils.url('Notifications-HandleForm') }" method="POST">
<div class="form-group">
<iscomment> checkbox </iscomment>
<div class="form-row">
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="notificationSales"
<isprint value="${pdict.notificationsForm.notificationSales.attributes}" encoding="off" />
/>
<label class="form-check-label" for="notificationSales">
${pdict.notificationsForm.notificationSales.label}
</label>
</div>
</div>
<iscomment> checkbox </iscomment>
<div class="form-row">
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="notificationNew"
<isprint value="${pdict.notificationsForm.notificationNew.attributes}" encoding="off" />
/>
<label class="form-check-label" for="notificationNew">
${pdict.notificationsForm.notificationNew.label}
</label>
</div>
</div>
<iscomment> checkbox </iscomment>
<div class="form-row">
<div class="form-check form-check-inline">
<input type="checkbox" class="form-check-input" id="notificationStock"
<isprint value="${pdict.notificationsForm.notificationStock.attributes}" encoding="off" />
/>
<label class="form-check-label" for="notificationStock">
${pdict.notificationsForm.notificationStock.label}
</label>
</div>
</div>
</div>
<iscomment> submit button </iscomment>
<div class="form-row">
<button type="submit" class="btn btn-primary">
${Resource.msg('button.submit.promo.code','cart', null)}
</button>
</div>
<iscomment> CSRF protection </iscomment>
<input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}" />
</form>
</div>

View File

@@ -0,0 +1,9 @@
<iscomment> total stock </iscomment>
<p class="text-primary mt-3">
Total Stock: ${pdict.product.stockInformation}
</p>
<!-- Availability -->
<div class="availability row product-availability" data-ready-to-order="${product.readyToOrder}" data-available="${product.available}">
<isinclude template="product/components/availability" />
</div>

View File

@@ -0,0 +1,18 @@
<div class="container">
<isif condition="${pdict.basket.allProductLineItems.length > 0}">
<ul class="list-group">
<iscomment>Assign product line item to variable product</iscomment>
<isloop items="${pdict.basket.allProductLineItems}" var="product">
<li class="list-group-item">
${product.productName} - ${product.price}
</li>
</isloop>
</ul>
<iselse/>
Your Basket is empty.
</isif>
</div>

View File

@@ -0,0 +1,35 @@
<div class="store-details" data-store-id="${store.ID}">
<div class="store-name">${store.name}</div>
<address>
<a class="store-map" target='_blank' href="https://maps.google.com/?daddr=${store.latitude},${store.longitude}">
${store.address1}
<isif condition="${store.address2}">
${store.address2}
</isif>
<isif condition="${store.city}">
${store.city},
</isif>
<isif condition="${store.stateCode}">
${store.stateCode}
</isif>
${store.postalCode}
</a>
<div class="store-hours">
<isif condition="${store.storeHours}">
<isprint value="${store.storeHours}" encoding="off"/>
</isif>
<isif condition="${store.email}">
<p>
<i class="fa fa-envelope" aria-hidden="true"></i>
<a href="mailto:${store.email}">${store.email}</a>
</p>
</isif>
</div>
<p>
<isif condition="${store.phone}">
<i class="fa fa-phone" aria-hidden="true"></i>
<span><a class="storelocator-phone" href="tel:${store.phone}">${store.phone}</a></span>
</isif>
</p>
</address>
</div>

View File

@@ -0,0 +1,3 @@
label.title=(En)You are Shopping at
label.featured=(En)Featured Product
label.stock=(En)Total Stock

View File

@@ -0,0 +1,3 @@
label.title=(日本語) You are Shopping at
label.featured=(日本語) Featured Product
label.stock=(日本語) total stock

View File

@@ -0,0 +1,6 @@
## Notification preferences, extends profile ##
title.notifications=Store Notification Settings
label.notification.sales=Recieve email notifications about upcoming sales
label.notification.new=Recieve email notifications about new products
label.notification.stock=Recieve email notifications about back in stock products
button.notification.submit=Save notification preferences

View File

@@ -0,0 +1,4 @@
## cartridge.properties for cartridge sacrifire
#Wed Nov 22 16:58:16 CEST 2021
demandware.cartridges.sacrifire.multipleLanguageStorefront=true
demandware.cartridges.sacrifire.id=the_learning_cartridge

View File

@@ -0,0 +1,8 @@
{
"hooks": [
{
"name": "app.notification.email",
"script": "./cartridge/scripts/hooks/notificationChangeEmail"
}
]
}

View File

@@ -0,0 +1,4 @@
{
"hooks": "./hooks.json"
}