sfc-utils 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js ADDED
@@ -0,0 +1,254 @@
1
+
2
+ // Check to see if this should serve the app version of the project
3
+ let appCheck = function(){
4
+ // Save current env
5
+ const env = process.env.GATSBY_DEPLOY_ENV
6
+
7
+ let appVersion = false
8
+ // If env reports app, then it's app by default
9
+ if (env === 'app'){
10
+ appVersion = true
11
+ }
12
+
13
+ // If we can access window, check to see if we're dealing with app version
14
+ if (typeof window !== "undefined"){
15
+ // Any link with ?fromRichie=1 will have paywall disabled
16
+ // Native in-app webviews will have a custom user agent, so check that too
17
+ if (window.location.href.indexOf('fromRichie=1') > -1 || navigator.userAgent.indexOf(' Richie/') > -1) {
18
+ appVersion = true
19
+ }
20
+ }
21
+
22
+ return appVersion
23
+ }
24
+
25
+ // Blend the HDN var with whatever is already present on the page
26
+ // Returns a string for injection into the head of the page
27
+ let blendHDN = function(meta){
28
+
29
+ if (!meta.PROJECT){
30
+ // If we don't have a properly formatted meta var coming in, this is a legacy template and needs settings pulled
31
+ let url_add = meta.url_add || ""
32
+ meta = getSettings()
33
+ meta.URL_ADD = url_add
34
+ }
35
+
36
+ // Set vars with the new object
37
+ let {
38
+ PAYWALL_SETTING,
39
+ URL_ADD,
40
+ MAIN_DOMAIN,
41
+ PROJECT: {
42
+ AUTHORS,
43
+ ANALYTICS_CREDIT,
44
+ TITLE,
45
+ HEARST_CATEGORY,
46
+ KEY_SUBJECTS,
47
+ SUBFOLDER,
48
+ OPT_SLASH,
49
+ SLUG,
50
+ MARKET_KEY,
51
+ ISO_PUBDATE,
52
+ ISO_MODDATE,
53
+ CANONICAL_URL
54
+ },
55
+ } = meta
56
+ BASE_DOMAIN = MAIN_DOMAIN
57
+ if (MARKET_KEY === "CT"){
58
+ BASE_DOMAIN = "ctinsider.com"
59
+ }
60
+ let siteDomain
61
+ if(CANONICAL_URL){
62
+ siteDomain = CANONICAL_URL.match('^(.+?).com')[0]
63
+ }
64
+ else{
65
+ siteDomain = MAIN_DOMAIN
66
+ }
67
+ // Check if we need a slash
68
+ let slash = OPT_SLASH
69
+
70
+ // Add the canonical here unless it's sent in
71
+ if (!CANONICAL_URL){
72
+ CANONICAL_URL = `${BASE_DOMAIN}/${SUBFOLDER}${slash}${SLUG}`
73
+ }
74
+
75
+ // If canonical has a slash at the end, remove it
76
+ if (CANONICAL_URL.slice(-1) === "/"){
77
+ CANONICAL_URL = CANONICAL_URL.slice(0, -1)
78
+ }
79
+
80
+ // Add the url add here unless it's sent in
81
+ if (!URL_ADD){
82
+ URL_ADD = ""
83
+ }
84
+
85
+ // If url add does not end with a /, add it
86
+ if (URL_ADD && URL_ADD.slice(-1) !== "/"){
87
+ URL_ADD += "/"
88
+ }
89
+
90
+ // Get dates from env
91
+ let pubdate = ""
92
+ if (ISO_PUBDATE){
93
+ pubdate = ISO_PUBDATE.toString().split('T')[0] + " 00:00:00"
94
+ }
95
+ let moddate = ""
96
+ if (ISO_MODDATE){
97
+ moddate = ISO_MODDATE.toString().split('T')[0] + " 00:00:00"
98
+ }
99
+
100
+ // Setting up vars
101
+ let HDN = {}
102
+ HDN.dataLayer = {}
103
+ HDN.dataLayer.content = {}
104
+ HDN.dataLayer.href = {}
105
+ HDN.dataLayer.source = {}
106
+ HDN.dataLayer.sharing = {}
107
+ HDN.dataLayer.presentation = {}
108
+ HDN.dataLayer.paywall = {}
109
+
110
+ // HDN.dataLayer object for content and href data
111
+ HDN.dataLayer.content.title = TITLE
112
+ HDN.dataLayer.content.subtitle = ''
113
+ HDN.dataLayer.content.objectId = `${SUBFOLDER}${slash}${SLUG}/${URL_ADD}`
114
+ HDN.dataLayer.content.objectType = 'project'
115
+
116
+ // Check if we have more than one HEARST_CATEGORY, split on commas
117
+ var categories = HEARST_CATEGORY.split(',').map(item=>item.trim())
118
+ HDN.dataLayer.content.sectionPath = categories
119
+
120
+ var key_subjects = KEY_SUBJECTS ? KEY_SUBJECTS.split(',').map(item=>item.trim()) : []
121
+ HDN.dataLayer.content.keySubjects = key_subjects
122
+ HDN.dataLayer.content.pubDate = pubdate
123
+ HDN.dataLayer.content.lastModifiedDate = moddate
124
+ HDN.dataLayer.content.wordCount = ''
125
+ HDN.dataLayer.content.keywords = []
126
+ HDN.dataLayer.content.keyPersons = []
127
+ HDN.dataLayer.content.keyOrganizations = []
128
+ HDN.dataLayer.content.keyConcepts = []
129
+ HDN.dataLayer.content.keyCategories = []
130
+ HDN.dataLayer.content.keyPlaces = []
131
+ HDN.dataLayer.content.keyNlpPerson = []
132
+ HDN.dataLayer.content.keyNlpLocation = []
133
+ HDN.dataLayer.content.keyNlpOrganization = []
134
+ HDN.dataLayer.content.keyNlpEvent = []
135
+ HDN.dataLayer.content.keyNlpWorkOfArt = []
136
+ HDN.dataLayer.content.keyNlpConsumerGood = []
137
+ HDN.dataLayer.content.keyNlpOther = []
138
+ HDN.dataLayer.content.keyNlpUnknown = []
139
+
140
+ // HDN.dataLayer object for source information
141
+ HDN.dataLayer.source.authorName = ''
142
+
143
+ HDN.dataLayer.source.authorTitle = '';
144
+ HDN.dataLayer.source.originalSourceSite = '';
145
+ HDN.dataLayer.source.publishingSite = '';
146
+ HDN.dataLayer.source.sourceSite = '';
147
+ switch(MARKET_KEY){
148
+ case "SFC":
149
+ HDN.dataLayer.source.authorTitle = 'San Francisco Chronicle Staff';
150
+ HDN.dataLayer.source.originalSourceSite = 'SF';
151
+ HDN.dataLayer.source.publishingSite = 'premiumsfgate';
152
+ HDN.dataLayer.source.sourceSite = 'sfgate';
153
+ break;
154
+ case "Houston":
155
+ HDN.dataLayer.source.authorTitle = 'Houston Chronicle Staff';
156
+ HDN.dataLayer.source.originalSourceSite = 'HC';
157
+ break;
158
+ case "SanAntonio":
159
+ HDN.dataLayer.source.authorTitle = 'Express News Staff';
160
+ HDN.dataLayer.source.originalSourceSite = 'EN';
161
+ break;
162
+ case "Albany":
163
+ HDN.dataLayer.source.authorTitle = 'Times Union Staff';
164
+ HDN.dataLayer.source.originalSourceSite = 'TU';
165
+ break;
166
+ case "CT":
167
+ HDN.dataLayer.source.authorTitle = 'Connecticut Digital Staff';
168
+ HDN.dataLayer.source.originalSourceSite = 'CT';
169
+ break;
170
+ }
171
+
172
+ // HDN.dataLayer object for sharing information
173
+ HDN.dataLayer.sharing.openGraphUrl =`${siteDomain}/${SUBFOLDER}${slash}${SLUG}/${URL_ADD}`
174
+ HDN.dataLayer.sharing.openGraphType = 'article'
175
+
176
+ // More page settings
177
+ HDN.dataLayer.href.pageUrl = `${siteDomain}/${SUBFOLDER}${slash}${SLUG}/${URL_ADD}`
178
+ HDN.dataLayer.href.canonicalUrl = `${CANONICAL_URL}/${URL_ADD}`
179
+
180
+ // HDN.dataLayer object for presentation information
181
+ HDN.dataLayer.presentation.hasSlideshow = ''
182
+ HDN.dataLayer.presentation.hasSlideshowListView = ''
183
+ HDN.dataLayer.presentation.hasVideo = ''
184
+ HDN.dataLayer.presentation.hasInteractive = ''
185
+
186
+ // HDN.dataLayer object for paywall information
187
+ HDN.dataLayer.paywall.premiumStatus = 'isPremium'
188
+ HDN.dataLayer.paywall.premiumEndDate = ''
189
+ HDN.dataLayer.paywall.policy = PAYWALL_SETTING
190
+
191
+ // Special site var
192
+ HDN.dataLayer.site = {
193
+ domain: siteDomain.replace("https://www.",""),
194
+ domainRoot: siteDomain.replace("https://www.","").replace(".com",""),
195
+ subDomain: 'www',
196
+ name: HDN.dataLayer.source.publishingSite,
197
+ property: HDN.dataLayer.source.originalSourceSite,
198
+ siteId: '35',
199
+ siteUrl: siteDomain,
200
+ timeZone: 'Pacific',
201
+ }
202
+
203
+ // Create author for analytics here
204
+ let authorString = ""
205
+ if (ANALYTICS_CREDIT !== ''){
206
+ authorString = ANALYTICS_CREDIT
207
+ } else if (AUTHORS){
208
+ // If one wasn't specified, use the one in the config
209
+ AUTHORS.forEach((author, index) => {
210
+ // Add author to string
211
+ authorString += author.AUTHOR_NAME
212
+ // Add comma if we're not done
213
+ if (index < (AUTHORS.length - 1)){
214
+ authorString += ", "
215
+ }
216
+ })
217
+ }
218
+ // If we didn't get any author, sub in default
219
+ if (authorString === ""){
220
+ authorString = HDN.dataLayer.source.authorTitle
221
+ }
222
+ HDN.dataLayer.source.authorName = authorString
223
+
224
+ let appVer = appCheck()
225
+ if (appVer){
226
+ HDN.dataLayer.paywall.mode = "edbDisabled"
227
+ }
228
+
229
+ let stringHDN = `
230
+ var HDN = HDN || {};
231
+ HDN.dataLayer = HDN.dataLayer || {};
232
+ HDN.dataLayer.content = Object.assign(HDN.dataLayer.content || {}, ${JSON.stringify(HDN.dataLayer.content)});
233
+ HDN.dataLayer.source = Object.assign(HDN.dataLayer.source || {}, ${JSON.stringify(HDN.dataLayer.source)});
234
+ HDN.dataLayer.sharing = Object.assign(HDN.dataLayer.sharing || {}, ${JSON.stringify(HDN.dataLayer.sharing)});
235
+ HDN.dataLayer.href = Object.assign(HDN.dataLayer.href || {}, ${JSON.stringify(HDN.dataLayer.href)});
236
+ HDN.dataLayer.paywall = Object.assign(HDN.dataLayer.paywall || {}, ${JSON.stringify(HDN.dataLayer.paywall)});
237
+ HDN.dataLayer.site = Object.assign(HDN.dataLayer.site || {}, ${JSON.stringify(HDN.dataLayer.site)});
238
+ `
239
+
240
+ return {
241
+ stringHDN: stringHDN
242
+ }
243
+ }
244
+
245
+ // Grab neighbor files
246
+ let { getBrands } = require('./brands')
247
+ let { getSettings } = require('./settings')
248
+ let { getNav } = require('./nav')
249
+ let { getSpecialNav } = require('./specialnav')
250
+ let { getFooter } = require('./footer')
251
+ let { getTopper } = require('./topper')
252
+ let { getBlueconic } = require('./blueconic')
253
+
254
+ module.exports = { appCheck, blendHDN, getSettings, getBrands, getNav, getSpecialNav, getFooter, getTopper, getBlueconic }
package/nav.js ADDED
@@ -0,0 +1,247 @@
1
+ let { getBrands } = require("./brands");
2
+
3
+ // Handle nav for various markets and include nav options for other links
4
+ let getNav = function (
5
+ meta,
6
+ urlAdd,
7
+ forceColor,
8
+ navLink,
9
+ navArray,
10
+ subInstead
11
+ ) {
12
+ // If we aren't passing meta in, we have to call getSettings here
13
+ if (!meta) {
14
+ let { getSettings } = require("./settings");
15
+ meta = getSettings();
16
+ }
17
+
18
+ // If a link object was not provided, make one
19
+ if (!navLink) {
20
+ navLink = {
21
+ url: "#___gatsby",
22
+ text: "Special Report",
23
+ target: "_self",
24
+ };
25
+ }
26
+
27
+ // If a navArray was provided, create the subnav
28
+ let subnav = "";
29
+ let dropdownIcon = "";
30
+ if (navArray && navArray.length > 0) {
31
+ subnav = `<ul id="subnav">`;
32
+ for (let i = 0; i < navArray.length; i++) {
33
+ subnav += `<li><a class="active" href="${navArray[i].url}" target="${navArray[i].target}"><span class="arrow-bullet">▶</span> ${navArray[i].text}</a></li>`;
34
+ }
35
+ subnav += `</ul>`;
36
+ // Add a dropdown icon
37
+ dropdownIcon = `<div class="dropdown-icon">▾</div>`;
38
+ }
39
+
40
+ // Extension to URL if passed in
41
+ if (!urlAdd) {
42
+ urlAdd = "";
43
+ }
44
+
45
+ let {
46
+ attributes: { marketPrefix, invert, subscribeLink },
47
+ } = getBrands(meta.PROJECT.MARKET_KEY);
48
+ // Handle various CT domains
49
+ if (typeof window !== "undefined") {
50
+ switch (window.location.origin) {
51
+ case "https://www.ctpost.com":
52
+ marketPrefix = "ct";
53
+ break;
54
+ case "https://www.nhregister.com":
55
+ marketPrefix = "nh";
56
+ break;
57
+ case "https://www.greenwichtime.com":
58
+ marketPrefix = "gt";
59
+ break;
60
+ case "https://www.stamfordadvocate.com":
61
+ marketPrefix = "st";
62
+ break;
63
+ case "https://www.thehour.com":
64
+ marketPrefix = "th";
65
+ break;
66
+ case "https://www.newstimes.com":
67
+ marketPrefix = "nt";
68
+ break;
69
+ case "https://www.middletownpress.com":
70
+ marketPrefix = "mp";
71
+ break;
72
+ case "https://www.ctinsider.com":
73
+ marketPrefix = "in";
74
+ break;
75
+
76
+ case "https://www.beaumontenterprise.com":
77
+ marketPrefix = "texcom/beau";
78
+ break;
79
+ case "https://www.lmtonline.com":
80
+ marketPrefix = "texcom/laredo";
81
+ break;
82
+ case "https://www.mrt.com":
83
+ marketPrefix = "texcom/mrt";
84
+ break;
85
+ case "https://www.myplainview.com":
86
+ marketPrefix = "texcom/plain";
87
+ break;
88
+
89
+ case "https://www.bigrapidsnews.com":
90
+ marketPrefix = "midcom/big";
91
+ break;
92
+ case "https://www.manisteenews.com":
93
+ marketPrefix = "midcom/mani";
94
+ break;
95
+ case "https://www.ourmidland.com":
96
+ marketPrefix = "midcom/mid";
97
+ break;
98
+ case "https://www.michigansthumb.com":
99
+ marketPrefix = "midcom/huron";
100
+ break;
101
+ case "https://www.recordpatriot.com":
102
+ marketPrefix = "midcom/benzie";
103
+ break;
104
+ case "https://www.theheraldreview.com":
105
+ marketPrefix = "midcom/hr";
106
+ break;
107
+ case "https://www.lakecountystar.com":
108
+ marketPrefix = "midcom/lc";
109
+ break;
110
+ case "https://www.thetelegraph.com":
111
+ marketPrefix = "midcom/alton";
112
+ break;
113
+ case "https://www.theintelligencer.com":
114
+ marketPrefix = "midcom/ed";
115
+ break;
116
+ case "https://www.myjournalcourier.com":
117
+ marketPrefix = "midcom/jv";
118
+ break;
119
+ }
120
+ }
121
+
122
+ // Default is white text on black nav (SFC style)
123
+ let invertClass = "";
124
+ let color = "white";
125
+ // If inverted, do black on white nav
126
+ // Only change things if color isn't forced to white
127
+ if (invert || forceColor === "white") {
128
+ invertClass = "invert";
129
+ color = "black";
130
+ }
131
+
132
+ let subfolder = "";
133
+ if (meta.PROJECT.SUBFOLDER) {
134
+ subfolder = meta.PROJECT.SUBFOLDER + "/";
135
+ }
136
+
137
+ let rightBlock = `
138
+ <div class="topper-nav-social">
139
+ <a
140
+ id="topper-nav-mail-icon"
141
+ title="Share via email"
142
+ href="mailto:?subject=${meta.PROJECT.TITLE}&body=${meta.PROJECT.DESCRIPTION}%0A%0A${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}">
143
+ <svg
144
+ width="24"
145
+ height="24"
146
+ viewBox="0 0 24 24"
147
+ fill="none"
148
+ xmlns="http://www.w3.org/2000/svg"
149
+ >
150
+ <path
151
+ fill-rule="evenodd"
152
+ clip-rule="evenodd"
153
+ d="M3.00977 5.83789C3.00977 5.28561 3.45748 4.83789 4.00977 4.83789H20C20.5523 4.83789 21 5.28561 21 5.83789V17.1621C21 18.2667 20.1046 19.1621 19 19.1621H5C3.89543 19.1621 3 18.2667 3 17.1621V6.16211C3 6.11449 3.00333 6.06765 3.00977 6.0218V5.83789ZM5 8.06165V17.1621H19V8.06199L14.1215 12.9405C12.9499 14.1121 11.0504 14.1121 9.87885 12.9405L5 8.06165ZM6.57232 6.80554H17.428L12.7073 11.5263C12.3168 11.9168 11.6836 11.9168 11.2931 11.5263L6.57232 6.80554Z"
154
+ fill="currentColor"
155
+ />
156
+ </svg>
157
+ </a>
158
+ </div>
159
+ <div class="topper-nav-social">
160
+ <a
161
+ id="topper-nav-facebook-icon"
162
+ title="Share on Facebook"
163
+ href="https://www.facebook.com/sharer/sharer.php?u=${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}"
164
+ target="_blank"
165
+ rel="noopener noreferrer"
166
+ >
167
+ <svg
168
+ width="24"
169
+ height="24"
170
+ viewBox="0 0 24 24"
171
+ fill="none"
172
+ xmlns="http://www.w3.org/2000/svg"
173
+ >
174
+ <path
175
+ d="M9.19795 21.5H13.198V13.4901H16.8021L17.198 9.50977H13.198V7.5C13.198 6.94772 13.6457 6.5 14.198 6.5H17.198V2.5H14.198C11.4365 2.5 9.19795 4.73858 9.19795 7.5V9.50977H7.19795L6.80206 13.4901H9.19795V21.5Z"
176
+ fill="currentColor"
177
+ />
178
+ </svg>
179
+ </a>
180
+ </div>
181
+
182
+ <div class="topper-nav-social">
183
+ <a target="_blank" rel="noopener noreferrer" id="twitter-icon" title="Share on Twitter" href="https://twitter.com/intent/tweet?url=${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}&text=${meta.PROJECT.TWITTER_TEXT}">
184
+ <svg
185
+ width="24"
186
+ height="24"
187
+ viewBox="0 0 248 204"
188
+ fill="none"
189
+ xmlns="http://www.w3.org/2000/svg"
190
+ >
191
+ <path
192
+ data-name="Twitter Logo"
193
+ fill="currentColor"
194
+ d="M222 51.29c.15 2.16.15 4.34.15 6.52 0 66.74-50.8 143.69-143.69 143.69A142.91 142.91 0 0 1 1 178.82a102.72 102.72 0 0 0 12 .72 101.29 101.29 0 0 0 62.72-21.66 50.53 50.53 0 0 1-47.18-35.07 50.35 50.35 0 0 0 22.8-.86 50.53 50.53 0 0 1-40.52-49.5v-.64a50.25 50.25 0 0 0 22.92 6.32 50.55 50.55 0 0 1-15.6-67.42 143.38 143.38 0 0 0 104.08 52.77 50.55 50.55 0 0 1 86.06-46.06 101.19 101.19 0 0 0 32.06-12.26 50.66 50.66 0 0 1-22.2 27.93 100.89 100.89 0 0 0 29-7.94A102.84 102.84 0 0 1 222 51.29z"
195
+ />
196
+ </svg>
197
+ </a>
198
+ </div>
199
+ `;
200
+
201
+ if (subInstead) {
202
+ rightBlock = `
203
+ <a class="sub-box" href="${subscribeLink}" target="_blank">
204
+ <div>Subscribe</div>
205
+ </a>
206
+ `;
207
+ }
208
+
209
+ let navHTML = `<nav class="topper-nav-container ${invertClass}">
210
+ <div class="topper-nav-left">
211
+ <a
212
+ href="/"
213
+ target="_blank"
214
+ rel="noopener noreferrer"
215
+ >
216
+ <div>
217
+ <img
218
+ class="topper-nav-desk-logo"
219
+ alt="Logo"
220
+ src="https://files.sfchronicle.com/static-assets/logos/${marketPrefix}-${color}.png"
221
+ ></img>
222
+ <img
223
+ class="topper-nav-mobile-logo"
224
+ alt="Logo"
225
+ src="https://files.sfchronicle.com/static-assets/logos/${marketPrefix}-square-${color}.png"
226
+ ></img>
227
+ </div>
228
+ </a>
229
+ <a
230
+ class="topper-nav-title"
231
+ id="nav-title"
232
+ href="${navLink.url}"
233
+ target="${navLink.target || "_blank"}"
234
+ >
235
+ ${navLink.text}${dropdownIcon}
236
+ </a>
237
+ </div>
238
+ <div class="topper-nav-right">
239
+ ${rightBlock}
240
+ </div>
241
+ ${subnav}
242
+ </nav>`;
243
+
244
+ return navHTML;
245
+ };
246
+
247
+ module.exports = { getNav };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "sfc-utils",
3
+ "version": "1.2.5",
4
+ "author": "ewagstaff <evanjwagstaff@gmail.com>",
5
+ "dependencies": {
6
+ "archieml": "^0.4.2",
7
+ "async": "^3.2.1",
8
+ "googleapis": "59.0.0",
9
+ "html-entities": "1.3.1",
10
+ "htmlparser2": "4.1.0",
11
+ "opn": "^6.0.0",
12
+ "write": "^2.0.0"
13
+ }
14
+ }
package/settings.js ADDED
@@ -0,0 +1,86 @@
1
+
2
+ /* Handle the data/processing for the Hearst analytics and paywall configuration */
3
+ let projectConfig;
4
+ try {
5
+ projectConfig = require("../../project-config.json")
6
+ } catch (err){
7
+ try {
8
+ projectConfig = require("../../project.json")
9
+ } catch(err){
10
+ // It's ok
11
+ }
12
+ }
13
+ let projectSettings = projectConfig.PROJECT
14
+
15
+ // Get settings off story_settings if it exists, otherwise fall back to projectConfig
16
+ let getSettings = function(){
17
+ let settings = projectConfig
18
+ // This needs to be set even if the "try" below fails
19
+ settings.PROJECT['ANALYTICS_CREDIT'] = ''
20
+ // Populate with storySettings if they exist
21
+ let storySettings
22
+ try {
23
+
24
+ try {
25
+ // Check for classic story_settings sheet
26
+ [storySettings] = require("../../src/data/story_settings.sheet.json")
27
+ } catch(err){
28
+ try {
29
+ // May be an Archie doc, try grabbing from there
30
+ storySettings = require("../../data/project_data.json").story_settings
31
+ } catch(err) {
32
+ // hacking this to bypass??
33
+ // Evan: This hack has my approval
34
+ }
35
+ }
36
+ // Populate with sheet settings
37
+ settings = {
38
+ "PAYWALL_SETTING": storySettings.Paywall,
39
+ "EMBEDDED": projectConfig.EMBEDDED,
40
+ "GOOGLE_SHEETS": projectConfig.GOOGLE_SHEETS,
41
+ "GOOGLE_DOCS": projectConfig.GOOGLE_DOCS,
42
+ "MAIN_DOMAIN": projectConfig.MAIN_DOMAIN,
43
+ "PROJECT": {
44
+ "SUBFOLDER": storySettings.Year,
45
+ "SLUG": storySettings.Slug,
46
+ "TITLE": storySettings.SEO_Title,
47
+ "DISPLAY_TITLE": storySettings.Title,
48
+ "SOCIAL_TITLE": storySettings.Social_Title,
49
+ "DECK": storySettings.Deck,
50
+ "URL": "https://projects.sfchronicle.com",
51
+ "IMAGE": "https://s.hdnux.com/photos/0/0/0/0/"+storySettings.Social_ImageID+"/1/1600x0.jpg",
52
+ "DESCRIPTION": storySettings.SEO_Description,
53
+ "TWITTER_TEXT": storySettings.Twitter_Text,
54
+ "DATE": storySettings.Publish_Date,
55
+ "MOD_DATE": storySettings.Mod_Date || storySettings.LastModDate_C2P,
56
+ "AUTHORS": projectSettings.AUTHORS,
57
+ "ANALYTICS_CREDIT": storySettings.Analytics_Credit,
58
+ "HEARST_CATEGORY": storySettings.Category || storySettings.Analytics_Section || "News",
59
+ "KEY_SUBJECTS": storySettings.Key_Subjects || "",
60
+ "MARKET_KEY": storySettings.Market_Key,
61
+ // Surveys have slightly different naming, so catch that below for backwards compat
62
+ "NEWSLETTER_ID": storySettings.NewsletterID || storySettings.Custom_Sailthru_ID || projectSettings.NEWSLETTER_ID,
63
+ "NEWSLETTER_PROMO": storySettings.NewsletterPromo || storySettings.Custom_Signup_Text || projectSettings.NEWSLETTER_PROMO,
64
+ "NEWSLETTER_LEGAL": storySettings.NewsletterLegal || storySettings.TOS_Text || projectSettings.NEWSLETTER_LEGAL,
65
+ "RELATED_LINKS_HED": storySettings.Related_Links_Hed
66
+ }
67
+ }
68
+ } catch (err){
69
+ // It's ok, we'll use project data
70
+ }
71
+
72
+ // Check if we need a slash
73
+ let slash = "";
74
+ if (settings.PROJECT.SUBFOLDER){
75
+ slash = "/"
76
+ }
77
+ settings.PROJECT['OPT_SLASH'] = slash
78
+ // Set the canonical (either from the sheet override or constructed)
79
+ settings.PROJECT['CANONICAL_URL'] = projectConfig.MAIN_DOMAIN + "/" + settings.PROJECT.SUBFOLDER + slash + settings.PROJECT.SLUG
80
+ if (typeof storySettings !== "undefined" && storySettings.Canonical_URL){
81
+ settings.PROJECT['CANONICAL_URL'] = storySettings.Canonical_URL
82
+ }
83
+ return settings
84
+ }
85
+
86
+ module.exports = { getSettings }