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