An error occurred while processing the template.
The following has evaluated to null or missing: ==> crdPageUrl [in template "307358#307392#284013" at line 361, column 46] ---- Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: ${crdPageUrl} [in template "307358#307392#284013" at line 361, column 44] - Reached through: #assign crdPageFullUrl = "${crdPageUr... [in template "307358#307392#284013" at line 361, column 17] ----
1<#-- STAFF PICKS TEMPLATE -->
2<#--Settings needed for local REST API call -->
3<#assign portalSiteID = "" /><#-- Portal side id needed to query records -->
4<#assign agencyID = "" /><#-- Agency ID needed for cover fetching. Can be obtained e.g. from SOLR -->
5<#assign agencyMemberID = "" />
6<#assign agencyName = "" /><#-- Needed for cover fetching -->
7<#assign portNumber = "16520" /><#-- Needed for all API calls -->
8<#assign queryLimit = 8 /><#-- Limits how many records are shown at maximum in list view -->
9<#assign portalUrl = "" />
10<#assign portalUrlSecure = "" /><#-- Used for fetching covers and get rid of mixed content warning -->
11<#assign csAddress = "http://arena-central" /> <#-- used to be arena-central:16517 -->
12<#assign lsAddress = "http://arena-local:16520" />
13<#assign friendlyUrl = "" />
14<#assign sitePrefix = "/web/arena" />
15
16<#-- If friendlyUrl is not set, get it from the organization -->
17<#if friendlyUrl == "" >
18 <#assign groupLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.GroupLocalService")/>
19 <#assign group = groupLocalService.getGroup(groupId)/>
20 <#assign friendlyUrl = group.getFriendlyURL() />
21 <#assign sitePrefix = "/web${friendlyUrl}" />
22</#if>
23
24<#-- Get server and page URLs -->
25<#assign serviceContext = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"].getServiceContext() />
26<#assign themeDisplay = serviceContext.getThemeDisplay() />
27<#assign portalUrl = themeDisplay.getPortalURL() />
28<#assign portalUrlSecure = portalUrl />
29<#if portalUrlSecure?index_of("https") == -1>
30 <#assign portalUrlSecure = portalUrl?replace("http", "https") />
31</#if>
32<#if portalUrl?index_of("https") != -1>
33 <#assign portalUrl = portalUrl?replace("https", "http") />
34</#if>
35
36<#assign virtualURL = portalUrlSecure?replace("https://","") />
37
38<#assign fullPageUrl = portalUrl + themeDisplay.getURLCurrent() />
39<#assign urlMatcher = themeDisplay.getURLCurrent()?matches("^(/[a-z]{2}(_[A-Z]{2})?)?/web/[^/]+")>
40<#list urlMatcher as algFriendlyUrl>
41 <#assign crdPageUrl = portalUrlSecure + algFriendlyUrl + "/results"/>
42 <#assign searchPageUrl = portalUrlSecure + algFriendlyUrl + "/search"/>
43</#list>
44
45<#-- General variables needed in multiple macros -->
46<#assign journalArticleResourceService = serviceLocator.findService("com.liferay.journal.service.JournalArticleResourceLocalService") />
47<#assign articleResourcePK = journalArticleResourceService.getArticleResourcePrimKey(groupId, .vars['reserved-article-id'].data)?number />
48<#assign journalArticleService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") />
49<#assign journalArticle = journalArticleService.getArticle(groupId, .vars['reserved-article-id'].data) />
50
51<#--Makes a query to the REST API and requests records. This function should be used to call the API within this template.
52Returns the result as a simpleHash object -->
53<#function queryAPI query limit>
54 <#assign fullRequestUrl = "${lsAddress}/local-rest/api/v1/portalsites/${portalSiteID}/records?query=${httpUtilUnsafe.encodeURL(query)}&count=${limit}&isShowExtended=false&sortDirection=Descending&sortField=Relevance&agencyMemberId=${agencyMemberID}&decorationNames=Ratings"/>
55 <#assign response = httpUtilUnsafe.URLtoString(fullRequestUrl) />
56 <#assign result = jsonFactoryUtil.looseDeserialize(response) />
57 <#return result />
58</#function>
59
60<#-- Makes a query to Central REST API to get portal site settings -->
61<#function getPortalSiteSettingsFromCentralService>
62 <#assign requestUrlPortalSites = "${csAddress}/central-rest/api/v1/configs/portalsites?vhost=${httpUtilUnsafe.encodeURL(virtualURL)}&friendlyUrl=${httpUtilUnsafe.encodeURL(friendlyUrl)}" />
63 <#assign response = httpUtilUnsafe.URLtoString(requestUrlPortalSites) />
64 <#assign result = jsonFactoryUtil.looseDeserialize(response) />
65 <#return result />
66</#function>
67
68<#-- Makes a query to Central REST API to get Member settings to extract Agency ID from -->
69<#function getAgencySettingsFromCentralService>
70 <#assign requestUrlMemberAgencies = "${csAddress}/central-rest/api/v1/configs/agencymembers/${agencyMemberID}" />
71 <#assign response = httpUtilUnsafe.URLtoString(requestUrlMemberAgencies) />
72 <#assign result = jsonFactoryUtil.looseDeserialize(response) />
73 <#return result />
74</#function>
75
76<#function getServerSettingsFromCentralService>
77 <#assign portalSites = "" />
78 <#assign returnValue = "OK" />
79 <#assign portalSites = "" />
80 <#assign memberData = "" />
81 <#assign favoriteAgencyName = "" />
82 <#attempt>
83 <#assign portalSites = getPortalSiteSettingsFromCentralService() />
84 <#if portalSites.id??>
85 <#assign portalSiteID = portalSites.id?string />
86 <#assign favoriteAgencyName = portalSites.mainGroup.properties["Favourite Agency Member"] />
87 <#else>
88 <#assign requestUrlPortalSites = "${csAddress}/central-rest/api/v1/configs/portalsites?vhost=${httpUtilUnsafe.encodeURL(virtualURL)}&friendlyUrl=${httpUtilUnsafe.encodeURL(friendlyUrl)}" />
89 <#assign response = httpUtilUnsafe.URLtoString(requestUrlPortalSites) />
90 </#if>
91 <#if favoriteAgencyName != "">
92 <#-- Go through portalSites.agencyMemberSummaries to find the one with matching favoriteAgencyName -->
93 <#list portalSites.agencyMemberSummaries as summary>
94 <#if summary.name == favoriteAgencyName>
95 <#assign agencyMemberID = summary.id?string />
96 </#if>
97 </#list>
98 </#if>
99 <#recover>
100 <#assign returnValue = "Portal Sites failed" />
101 </#attempt>
102 <#if agencyMemberID != "" && returnValue == "OK">
103 <#attempt>
104 <#assign memberData = getAgencySettingsFromCentralService() />
105 <#if memberData.agency??>
106 <#assign agencyID = memberData.agency.id />
107 <#assign agencyName = memberData.agency.name />
108 </#if>
109 <#recover>
110 <#assign returnValue = "Agency member data failed" />
111 </#attempt>
112 </#if>
113
114 <#return returnValue />
115</#function>
116
117<#macro debugServerInfo>
118 <div class="serverInfo hidden debug informative">
119 <p class="idsForRestAPI">
120 <strong>Portal Site ID:</strong> ${portalSiteID}<br />
121 <strong>Agency ID:</strong> ${agencyID}<br />
122 <strong>Agency name:</strong> ${agencyName}<br />
123 <strong>Member ID:</strong> ${agencyMemberID}
124
125 </p>
126 <p class="serverURL">
127 <strong>Portal site URL:</strong> ${portalUrl}<br />
128 <strong>Portal site URL Secure:</strong> ${portalUrlSecure}<br />
129 <strong>Virtual URL:</strong> ${virtualURL}<br />
130 <strong>Friendly URL:</strong> ${friendlyUrl}
131 </p>
132 </div>
133</#macro>
134
135<#-- Outputs a hidden debug tag with information. -->
136<#macro debugthis title message link isinformative>
137 <#assign cssClass = "hidden debug" />
138 <#if isinformative>
139 <#assign cssClass = "hidden debug informative" />
140 </#if>
141 <div class="${cssClass}">
142 <h4>Debug - ${title}</h4>
143 <#if link == "">
144 <p>${message}</p>
145 <#else>
146 <p><a href="${link}">${message}</a></p>
147 </#if>
148 </div>
149</#macro>
150
151<#macro showTags>
152 <#assign tagsService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetTagLocalService") />
153 <#assign tagsAsset = tagsService.getTags("com.liferay.journal.model.JournalArticle", articleResourcePK) />
154
155<#-- Services for tag links -->
156 <#assign layoutUid = journalArticle.getLayoutUuid() />
157
158 <h3><@liferay.language key="staffpicks.detail.header.tags" /></h3>
159 <@debugthis title="tags" message="Tag links don't work yet." isinformative=true link="" />
160 <@debugthis title="tags" message=layoutUid isinformative=true link="" />
161 <ul class="staffpick-article-tags">
162 <#list tagsAsset as tag>
163 <#assign tagNameLink = tag.getName() />
164 <li class="staffpick-article-tagentry"><a class="staffpicks-article-tag-link" href="${sitePrefix}/-/tag/${tagNameLink}">${tagNameLink}</a></li>
165 </#list>
166 </ul>
167</#macro>
168
169<#macro showArticleAuthor>
170 <#if articleAuthor?? && articleAuthor.getData() != "">
171 <p class="staffpick-article-author-name">
172 <@liferay.language_format key="staffpicks.detail.articleauthor" arguments=[articleAuthor.getData()] />
173 <#--${languageUtil.format(locale, "staffpicks.detail.articleauthor", [articleAuthor.getData()])}-->
174 </p>
175 </#if>
176</#macro>
177
178<#macro showDate dateString>
179 <#assign originalLocale = locale />
180 <#setting locale = localeUtil.getDefault() />
181 <#assign time = dateString?datetime("EEE, dd MMM yyyy hh:mm:ss ZZZZ") />
182 <#setting locale = originalLocale />
183 <#assign dateFormat = "dd MMM yyyy" />
184 <#if stringUtil.equals(locale, "en_US")>
185 <#assign dateFormat = "MM/dd/yyyy" />
186 <#elseif stringUtil.equals(locale, "en_GB")>
187 <#assign dateFormat = "MMM d yyyy" />
188 <#elseif stringUtil.equals(locale, "de_DE")>
189 <#assign dateFormat = "dd.MM.yy" />
190 <#elseif stringUtil.equals(locale, "fi_FI")>
191 <#assign dateFormat = "d.M.yy" />
192 <#elseif stringUtil.equals(locale, "fr_FR")>
193 <#assign dateFormat = "dd/MM/yy" />
194 <#elseif stringUtil.equals(locale, "nb_NO")>
195 <#assign dateFormat = "dd.MM.yy" />
196 <#elseif stringUtil.equals(locale, "ru_FI")>
197 <#assign dateFormat = "dd.MM.yy" />
198 <#elseif stringUtil.equals(locale, "sv_SE")>
199 <#assign dateFormat = "yyyy-MM-dd" />
200 </#if>
201 ${dateUtil.getDate(time, dateFormat, locale)}
202</#macro>
203
204<#macro showRecord recordInfo>
205<#-- Arena 4 CRD link -->
206 <#assign crdPageFullUrl = "${crdPageUrl}?p_p_id=crDetailWicket_WAR_arenaportlet&p_p_lifecycle=1&p_p_state=normal&p_r_p_arena_urn%3Aarena_search_item_id=${recordInfo.id}&p_r_p_arena_urn%3Aarena_agency_name=${agencyName}" />
207
208 <li class="similar-record record-${recordInfo.id}">
209 <@debugthis title="Record" message="ID: ${recordInfo.id}" link="" isinformative=true />
210 <a class="similar-record-link" href="${crdPageFullUrl}">
211 <img class="similar-record-cover" src="${portalUrlSecure}/local-rest/api/v1/portalsites/${portalSiteID}/agencies/${agencyID}/records/${httpUtilUnsafe.encodeURL(recordInfo.id)}/cover" alt="" />
212 <span class="similar-record-title">${recordInfo.fields.title}</span>
213 <#if recordInfo.fields.authors?? && recordInfo.fields.authors?size gt 0 && recordInfo.fields.authors[0]??>
214 <span class="similar-record-author">${recordInfo.fields.authors[0].name}
215 </#if>
216 </a>
217 <#if recordInfo.rating??>
218 <@showRatings rating=recordInfo.rating recordtitle=recordInfo.fields.title />
219 </#if>
220 </li>
221</#macro>
222
223<#macro showRatings rating recordtitle>
224 <#assign ratingFive = (rating/2) />
225 <#assign ratingLimit = 5 />
226 <div class="staffpicks-rating">
227 <#assign ratingFiveString = ratingFive?string.number />
228 <#list 1..ratingLimit as x>
229 <#if (x-0.5) == ratingFive>
230 <span class="arena-rating rating-half-full"><i class="icon-star-half-full"></i></span>
231 <#elseif x <= ratingFive>
232 <span class="arena-rating rating-full"><i class="icon-star"></i></span>
233 <#else>
234 <span class="arena-rating rating-empty"><i class="icon-star-empty"></i></span>
235 </#if>
236 </#list>
237 </div>
238</#macro>
239
240<#macro showSimilarRecords result>
241 <h3><@liferay.language key="staffpicks.detail.header.similartitles" /></h3>
242 <#assign searchString = "" />
243 <#attempt>
244 <#if result.fields.subjects?size gt 0>
245 <#assign searchString = searchString + "(" />
246 <#list result.fields.subjects as subject>
247 <#assign searchString = searchString + "subject:\"${subject}\"" />
248 <#if subject_index < result.fields.subjects?size-1>
249 <#assign searchString = searchString + " OR " />
250 </#if>
251 </#list>
252 <#assign searchString = searchString +") AND (" />
253 <#else>
254 <#assign searchString = searchString +"(" />
255 </#if>
256 <#recover>
257 </#attempt>
258 <#attempt>
259 <#if result.fields.languages?size gt 0>
260 <#list result.fields.languages as language>
261 <#assign searchString = searchString + "language:\"${language}\"" />
262 <#if language_index < result.fields.languages?size-1>
263 <#assign searchString = searchString + " OR " />
264 </#if>
265 </#list>
266 </#if>
267 <#recover>
268 </#attempt>
269 <#attempt>
270 <#assign searchString = searchString +") AND " />
271 <#assign searchString = searchString+"mediaclass:\"${result.fields.mediaClass}\" AND " />
272 <#assign searchString = searchString +"NOT uberkey:\"${result.uberkey}\"" />
273 <#recover>
274 </#attempt>
275
276 <#assign similarBooks = "">
277
278 <#attempt>
279 <#assign similarBooks = queryAPI(searchString, queryLimit) />
280 <ul class="staffpick-similar-records">
281 <#list similarBooks.records as record>
282 <@showRecord recordInfo = record />
283 </#list>
284 </ul>
285 <#recover>
286 <@debugthis title="Similar records" message="Similar records not looked for because an error happened in API call or processing it." isinformative=false link="" />
287 </#attempt>
288 <@debugthis title="Similar records search string" message=searchString isinformative=true link="" />
289 <#assign fullRequestUrl = '${lsAddress}/local-rest/api/v1/portalsites/${portalSiteID}/records?query=${searchString}&count=${queryLimit}&isShowExtended=false&sortDirection=Descending&sortField=Relevance&agencyMemberId=${agencyMemberID}'/>
290 <@debugthis title="Similar records search URL" message="Similar records query" isinformative=true link=fullRequestUrl />
291</#macro>
292
293<#-- Prints a debug button that toggles the visibility of elements with debug CSS class -->
294<#macro toggleDebug>
295 <script type="text/javascript">
296 function toggleDebugMessages() {
297 YUI().use("node",function(Y) {
298 Y.all(".debug").each(function() {
299 this.toggleClass("hidden");
300 });
301 });
302 };
303 </script>
304 <a href="javascript:void(0);" onclick="toggleDebugMessages()" class="btn btn-info toggle-debug-button"><i class="icon-eye-open"></i> Debug</a>
305</#macro>
306
307<link rel="stylesheet" type="text/css" href="//cdn.axiell.com/arena/staffpicks/staffpicks.css">
308<div class="staffpick-article">
309 <#assign status = "OK" />
310 <#assign status = getServerSettingsFromCentralService() />
311
312 <@debugServerInfo />
313
314 <#if status != "OK">
315 <@debugthis isinformative=false title="Central Service Settings failed" message="Failed with ${status}" link=""/>
316 </#if>
317
318 <div class="back-button-container">
319 <a class="staffpick-back-link" href="javascript:void(0);" onclick="window.history.back();"><@liferay.language key="back" /></a>
320 </div>
321 <style>.portlet-asset-categories-navigation, .staff-picks-introduction { display: none; }</style>
322 <#assign searchQuery = "" />
323 <#assign result = "" />
324 <#assign recordInfo = "" />
325 <#if recordId?? && recordId.getData() != "">
326 <#if recordId.getData()?index_of("id:") == -1>
327 <#assign searchQuery = 'id:"${recordId.getData()}"' />
328 <#else>
329 <#assign searchQuery = recordId.getData() />
330 </#if>
331 <#attempt>
332 <#assign result = queryAPI(searchQuery, 1) />
333 <#assign recordInfo = result.records[0] />
334
335 <#recover>
336 <#assign recordInfo = {
337 "fields" : {
338 "title" : "${.vars['reserved-article-title'].data}"
339 }
340 } />
341 <#assign fullRequestUrl = "${lsAddress}/local-rest/api/v1/portalsites/${portalSiteID}/records?query=${httpUtilUnsafe.encodeURL(searchQuery,true)}&count=1&isShowExtended=false&sortDirection=Descending&sortField=Relevance&agencyMemberId=${agencyMemberID}"/>
342 <@debugthis isinformative=false title="API call failed with this url" message=fullRequestUrl link=fullRequestUrl />
343
344 </#attempt>
345 <#else>
346 <#assign recordInfo = {
347 "fields" : {
348 "title" : "${.vars['reserved-article-title'].data}"
349 }
350 } />
351 </#if>
352
353
354
355
356 <div class="row">
357 <div class="col-xs-12 col-sm-4 col-lg-3 cover-column">
358
359 <#if recordInfo.id??>
360 <#--Arena 4 CRD link -->
361 <#assign crdPageFullUrl = "${crdPageUrl}?p_p_id=crDetailWicket_WAR_arenaportlet&p_p_lifecycle=1&p_p_state=normal&p_r_p_arena_urn%3Aarena_search_item_id=${recordInfo.id}&p_r_p_arena_urn%3Aarena_agency_name=${agencyName}" />
362
363 <a class="record-link" href="${crdPageFullUrl}">
364 <#assign assetService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") />
365 <#assign thumbnailPath = "" />
366 <#attempt>
367 <#assign serviceContext = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"].getServiceContext()>
368 <#assign themeDisplay = serviceContext.getThemeDisplay() />
369 <#assign thumbnailPath = journalArticle.getArticleImageURL(themeDisplay) />
370 <#recover>
371 </#attempt>
372 <#if journalArticle.getSmallImage() && thumbnailPath != "">
373 <img class="cover-image cover-overwrite" alt="" src="${thumbnailPath}" />
374 <#else>
375 <img class="cover-image" alt="" src="${portalUrlSecure}/local-rest/api/v1/portalsites/${portalSiteID}/agencies/${agencyID}/records/${httpUtilUnsafe.encodeURL(recordInfo.id)}/cover" />
376 </#if>
377 <span class="alg-button alg-button--inverted gotoCRDButton"><@liferay.language key="staffpicks.detail.viewbook" /></span>
378 </a>
379 </#if>
380 <@toggleDebug />
381 </div>
382 <div class="col-xs-12 col-sm-8 col-lg-9 staffpick-detail">
383 <div class="staffpick-article-title">
384 <h2>${recordInfo.fields.title}</h2>
385 </div>
386 <#-- Special debug which cannot be in a regular macro -->
387 <#if recordInfo.id??>
388 <@debugthis title="Record Id - API" message="Record ID from API: ${recordInfo.id}" isinformative=true link="" />
389 </#if>
390 <@debugthis title="Record Id - Query" message="Record ID from Query: ${recordId.getData()}" isinformative=true link="" />
391 <#if recordInfo.id?? && (recordInfo.id != recordId.getData() )>
392 <@debugthis title="Record id missmatch" message="Query ID and API ID do not match!" isinformative=false link="" />
393 </#if>
394
395 <#if recordInfo.fields?? && recordInfo.fields.authors?? && recordInfo.fields.authors?size gt 0 && recordInfo.fields.authors[0]??>
396 <div class="staffpick-article-author">
397 <a class="staffpick-article-author-link" href="${searchPageUrl}?p_p_id=searchResult_WAR_arenaportlet&p_p_lifecycle=1&p_p_state=normal&p_r_p_arena_urn%3Aarena_facet_queries=&p_r_p_arena_urn%3Aarena_search_query=${recordInfo.fields.authors[0].name}&p_r_p_arena_urn%3Aarena_search_type=solr&p_r_p_arena_urn%3Aarena_sort_advice=field%3DRelevance%26direction%3DDescending">
398 <@liferay.language_format key="staffpicks.detail.author" arguments=[recordInfo.fields.authors[0].name] />
399 </a>
400 </div>
401 </#if>
402 <div class="staffpick-article-text">
403 ${articleText.getData()}
404 </div>
405 <div class="staffpick-article-recommendedby-date row">
406 <div class="staffpick-article-recommeded-by col-xs-12 col-sm-7">
407 <@showArticleAuthor />
408 </div>
409 <div class="staffpick-article-date col-xs-12 col-sm-5">
410 <@showDate dateString=.vars['reserved-article-create-date'].data />
411 </div>
412 </div>
413 </div>
414 </div>
415 <#--<div class="row tags-container">
416 <div class="col-xs-12">
417 <@showTags />
418 </div>
419 </div>-->
420 <div class="row similar-records-container">
421 <div class="col-xs-12">
422 <@showSimilarRecords result=recordInfo />
423 </div>
424 </div>
425</div>