How do you create a news archive in umbraco? - umbraco

I'm trying to create a news archive in umbraco, where it would display a list of news pages organised by month. Each page would display a listing of the news items of that month.
I've followed the tutorial for creating news items, but I'm not sure how to create an archive. There doesn't seem to be any references to do this online either. Surely it's a common use case for the CMS.
Anyone have any ideas (preferably in razorscript if coding is required)?

I ended up using the datefolders package, which automatically sorts the news items into the correct year and month folder based on a predefined date field, much like #amelvin response. The benefit of datefolders is that it simplifies the user having to manually sort their articles into the right folder and maintain that organisation. They can just right click the news container item, create the article, set the date, and it'll just appear in the right folder. Also, it changes the folder when the date is changed.
In terms of the display of the archives, I had the following razor code, where NewsListing is the news items listing document type, and NewsItem is the news item document type:
Archive listing in the sidebar
<umbraco:Macro runat="server" language="cshtml">
#{
dynamic newsListingNode = Model.AncestorOrSelf(1).DescendantsOrSelf("NewsListing").First();
}
<div class="archive">
<ul>
#foreach (var newsYear in newsListingNode.Children)
{
foreach (var newsMonth in newsYear.Children)
{
#* Use format time to get the month string *#
dynamic dateLabel = umbraco.library.FormatDateTime(newsYear.Name + " " + newsMonth.Name + " 01", "MMMM yyyy");
<li>#dateLabel»</li>
}
}
</ul>
</div>
</umbraco:Macro>
Month archive page
<umbraco:Macro runat="server" language="cshtml">
#* Check the it is a month folder *#
#if ((#Model.NodeTypeAlias == "NewsDateFolder") &&
(#Model.Up().NodeTypeAlias == "NewsDateFolder") &&
(#Model.Up().Up().NodeTypeAlias == "NewsListing"))
{
dynamic newsMonth = Model;
dynamic newsYear = Model.Up();
dynamic dateLabel = umbraco.library.FormatDateTime(newsYear.Name + " " + newsMonth.Name + " 01", "MMMM yyyy");
<div class="news">
<h2>News archive: #dateLabel</h2>
#{
dynamic newsItems = Model.DescendantsOrSelf("NewsItem").OrderBy("sortDate desc");
}
#foreach(var newsItem in newsItems) {
<div class="block-content">
<h5>#newsItem.Name</h5>
<p>#newsItem.summaryText</p>
more»
</div>
}
</div>
}
</umbraco:Macro>

The most obvious solution in Umbraco would be to arrange your folder structure in the 'Content' section to match how you want the site to arrange items and then use simple xsl transformations to build the index pages.
Code two templates ('Settings' section), one to show the index for each month and another to display the individual article.
So a folder structure like:
> Content
> |--Home
> |--2011
> |--01 January
> |--News article 1
> |--News article 2
> |--News article 3
> |--News article 4
> |--02 February
> |--News article 5
> |--News article 6
> |--News article 7
Then use some simple xsl added as a macro to the index template (similar to the following) to run round the news articles (I got this from umbNewsListItems.xslt which I think came from the news sample site):
<xsl:template match="/">
<!-- The fun starts here -->
<div class="newsList">
<xsl:for-each select="$currentPage/* [#isDoc and string(umbracoNaviHide) != '1']">
<xsl:sort select="#updateDate" order="ascending" />
<h3 class="headline"> <a href="{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
</h3>
<small class="meta">Posted: <xsl:value-of select="umbraco.library:LongDate(#updateDate, true(), ' - ')"/></small><br/>
<p class="introduction">
<xsl:value-of select="umbraco.library:ReplaceLineBreaks(introduction)" disable-output-escaping="yes"/>
</p>
</xsl:for-each>
</div>
</xsl:template>

Related

Thymeleaf : 2 loops and an if statement inside a single element

In thymeleaf, I have a for each loop like so:
<ul class="days">
<li th:each="day : ${days}" th:text="${day}" >1</li>
</ul>
This successfully lists all of the strings in an array called days which is populated like ["1","2","3"..."31"] representing the days in a given month.
I also have an array of items which also contains days as strings.
Here is what I want to do in pseudo code but am struggling to figure out how to achieve it:
For each day in days; For each day in items ; if items.day =
days.day then set 'class=active' (bootstrap) and th:text =days.day
else
th:text=days.day
So if theres a day in items that matches a day in days then the <li> element is set to class=active and make the <li> clickable with href="/myurl". And either way the day from days is the th:text of the <li>.
Sorry if thats hard to understand, I tried to make it as clear as I could.
EDIT:
This is latest attempt:
<ul class="days">
<li th:each="s : ${days}" th:with="found=${false}">
<span th:each="item : ${items}" th:if="{$item.day == s}">
<span th:text="${s}" th:classappend="active" th:href
th:with="${found} = true"></span>
</span>
<span th:if="{$found == false}">
<span th:text="${s}"></span>
</span>
</li>
</ul>
I think you can achieve this with conditional expressions. Simply write something like
<span th:text="${s}" th:classappend="${#lists.contains(items, s)}? 'active' : ''">
This code also uses #lists, a very useful feature of Thymeleaf.

parse nested li inside ul and ol

I have a scenario in which when li comes under ul I need to replace it with a dot(.) and when li comes and ol I need to replace it with a number.
But the problem is-
1) It is not doing for nested li
2) It is appending at the same level. Same level means as soon as it finds li it will first add dot(.) and then it will add number.
What I want
1) Whenever li comes inside ul it should add dot(.).
2) Whenever li comes inside ol it should add a number.
data = "<ol>\n<li>Introduction\n<ol>\n<li>hyy ssss</li>\n</ol>\n</li>\n<li>Description</li>\n<li>Observation</li>\n<li>Results</li>\n<li>Summary</li>\n</ol>\n<ul>\n<li>Introduction</li>\n<li>Description\n<ul>\n<li>Observation\n<ul>\n<li>Results\n<ul>\n<li>Summary</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>Overview</li>\n</ul>\n<p>All the testing regarding bullet points would have been covered with the above content. Hence publishing this content will make an entry in in the selected page, cricket page and so on.</p>\n"
content = Nokogiri::HTML.parse(data)
content.at('ul').children.xpath("//li").each { |li| li.inner_html="\u2022 "+li.inner_html }
content.at('ol').children.xpath("//li").each_with_index { |li,index| li.inner_html="#{index} "+li.inner_html }
Perhaps you need this:
content.css('ol').reverse.each do |ol|
ol.css('> li').each_with_index { |li,index| li.inner_html="#{index + 1} "+li.inner_html }
end
content.css('ul > li').reverse.each { |li| li.inner_html="\u2022 "+li.inner_html }
puts content
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<ol>
<li>1 Introduction
<ol>
<li>1 hyy ssss</li>
</ol>
</li>
<li>2 Description</li>
<li>3 Observation</li>
<li>4 Results</li>
<li>5 Summary</li>
</ol>
<ul>
<li>• Introduction</li>
<li>• Description
<ul>
<li>• Observation
<ul>
<li>• Results
<ul>
<li>• Summary</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>• Overview</li>
</ul>
</body></html>
Reason of doing reverse -
Consider the dom:
<ul>
<li>Description
<ul>
<li>Observation</li>
</ul>
</li>
</ul>
When you do content.css('ul > li'), you get in order of [description, observation]. Without reverse, when you run the snippet, you change the description, but doing so will also change the object_id of observation node. Then you changed the observation node which is not referenced anywhere in content. That's why, I reversed it and acquired children before parents. By doing this, I made sure I'm changing the child first and then changed the parent so parent was aware of the change in child and there is no unreferenced node anywhere.
Suppose description's node id is 1234 and observation node_id is 2345. When you mutated description, it changed itself but also changed it's child(2345). New object id can be 3456 and 4567 respectively. Then you changed 2345 (by iteration), but it makes no effect because your content is showing 3456 -> 4567
Hope this makes sense.

Displaying attached image with post how to i get it to display

<div>
<div class="col-md-4">
<h3 class="textStrong">Latest Tweets</h3>
<a class="twitter-timeline" href="https://twitter.com/RFUK">Tweets by RFUK </a></div>
</div>
<div class="col-md-4"></div>
<div class="col-md-4">
<h2>News Feeds</h2>
#{
var news = new List<Piranha.Entities.Post>();
using (var db = new Piranha.DataContext()) {
news = db.Posts
.Include(p => p.CreatedBy)
.Where(p => p.Template.Name == "News Post Types")
.OrderByDescending(p => p.Published)
.Take(4).ToList();
}
}
#foreach (var post in news) {
<div class="post">
<h2>#post.Title</h2>
<p class="meta">Published #post.Published.Value.ToString("yyyy-MM-dd") by #post.CreatedBy.Firstname</p>
<p>#post.Excerpt</p>
<img src="#post.Attachments">
</div>
I working with posts. I have this code to work with.... Works really well I might add.. However the attached image I wish to display with the post. How can I do that?
<img src="#post.Attachments">
It doesn't appear to work any suggestions
on how I sort what I need to do?
Like #andreasnico pointed out Attachments is a collection of referenced media asset id's. If you want to display the first attachment (assuming you know it's an image) you'd probably do like this.
#foreach (var post in news) {
<div class="post">
<h2>#post.Title</h2>
<p class="meta">Published #post.Published.Value.ToString("yyyy-MM-dd") by #post.CreatedBy.Firstname</p>
<p>#post.Excerpt</p>
#if (post.Attachments.Count > 0) {
<img src="#UI.Content(post.Attachments[0])">
}
</div>
}
This would get the content URL for the first attachment and use it as the source to the image. Note that you can also scale & crop images for use in lists like this with:
<img src="#UI.Content(post.Attachments[0], 300, 100)">
This would scale & crop the image to be 300px wide & 100px high. You can read more about this here: http://piranhacms.org/docs/api-reference/ui-helper
Also if the page displaying the post list is controlled by the CMS and has a page type I'd suggest you look into adding either a PostRegion or PostModelRegion to that page. These region automatically loads a collection of post into the page model, you can specify the amount, sort order & some other stuff. This will simplify you reusing the page type but for example changing which type of post to display for different page instances.
Regards
Håkan

OR condition in Grails Shiro tag

I am doing a two level menu and I want to show some entries if the user has certain permissions.
I dont want to show the first level of the menu if the user doesn't have permissions to any other submenu because it would display an empty list of results.
I'll put some code to explain it better. This could be an example of the menu:
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
Is it there a way to wrap the <ul> somehow to ask if the user has foo or bar permission?
I'm using Grails 2.4.4 and Shiro plugin 1.2.1
UPDATE WITH SOLUTION:
Finally I've created a new tagLib that extends the ShiroTagLib and has the desired function:
class ShiroTagLib extends org.apache.shiro.grails.ShiroTagLib {
/**
* This tag only writes its body to the output if the current user
* have any of the given permissions provided in a separated comma String.
*/
def hasAnyPermission = { attrs, body ->
def permissions = attrs["permissions"]
if (!permissions){
throwTagError("Tag [hasAnyPermission] must have [permissions] attribute")
}
def permissionsList = permissions.split(',')
for (permission in permissionsList) {
if (checkPermission([permission: permission], "hasPermission")) {
// Output the body text.
out << body()
return out
}
}
}
}
with this you can do <shiro:hasAnyPermission permissions="foo,bar">
There are several ways to approach this but the hasAnyRole tag from the Shiro plugin is likely the easiest way.
<shiro:hasAnyRole in="foo,bar">
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
</shiro:hasAnyRole>
You can take a look at the source code for the tag library for more information.
Update
Based on the comment provided there is another option which is based on permissions.
<g:set var="hasSomePermission" value="${false}" />
<shiro:hasPermission permission="foo">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
...
<g:if test="${hasSomePermission}">
...
</g:if>
The Shiro plugin doesn't provide a logical OR tag for permissions like it does with roles, so the above is another approach. Using this same idea you could DRY it up with your own tag library.

Grails 2.1 createCriteria issue with params

edit-
I am trying to return a list of portfolios, along with the last 5 publications attached to each portfolio from my 2 domain classes. I am getting the last total 5 publications back and each list displays all 5. The query is not returning that particular instances own publications. Kellys great ideas put back into another track.
I have created the method in the portfolio controller which is the hasMany side to the publications which belongsTo the portfolio domain class.
I just cant seem to get the portfolios to list their own publications. If I change the eq portfolios, it all works fine, except each portfolio list shows the same publications.
How do I load each portfolio and list the last 5 publications. This is rendered from the portfolio/list page as a partial.Is this the issue maybe. Should I just render it from a new view which is not associated with the portfolio list action??
Newbie to grails and have read and read the doc's, just cant seem to get the params query to return correctly. Help
def _webList (){
//def per = Portfolio.properties
def portfolios = Portfolio.list(params.id)
def results = Publication.withCriteria {
eq('published', 'Yes')
order('lastUpdated', 'desc')
maxResults(5)
}
def reportscount = Publication.count()
[ portfolios: portfolios, results: results, reportscount: reportscount]
}
I can show the the sql log if needed.
EDIT
The following code is the entire partial from file _webList.gsp. The top div -alert loads on the page, but the content within the div property-list portfolio fails to load. Using Kelly's hibernate criteria produces the query in the sql log but not results or styles or anything are return to the view??. weird.!
<div class="alert alert-info" xmlns="http://www.w3.org/1999/html">Permissions apply to <strong>editing</strong> publications.<br>
<div style="display: inline;"><p>Click portfolio name to read or edit publications. Total number of sites: <strong>${rsNumb}</strong> | Total number of publications: <strong>${reportscount}</strong> </p>
</div>
</div>
<div class="property-list portfolio">
<g:each in="${portfolios}" var="portfolioInstance">
<div class="site-listing">
<div><span class="label">Site Name:</span><g:link action="show" id="${portfolioInstance?.id }">${portfolioInstance?.portfolioName?.encodeAsHTML()}</g:link></div>
<div><span class="label">Site Description: </span>${portfolioInstance?.portdescrip?.encodeAsHTML() }</div> <br>
<div><span class="label">Site Administrator: </span>${portfolioInstance?.profile?.portfolioAdmin?.encodeAsHTML() }</div> <br>
<div><span class="label"> Total publications:</span><span class="badge badge-success"> ${portfolioInstance?.publications?.size()}</span> </div>
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:if test="${portfolioInstance?.publications}">
<g:set var="publicationInstance" />
<ul class="site-publication">
<li class="fieldcontain">
<span id="publications-label" class="property-label"><g:message code="portfolio.publications.label" default="Last 5 published publications:" /></span>
<g:each in="${portfolioInstance.publications}" var="publicationInstance">
${publicationInstance?.id}
<span class="property-value" aria-labelledby="publications-label"><g:link controller="publication" action="show" id="${publicationInstance.id}">${publicationInstance?.encodeAsHTML()}</g:link></span>
<!-- and again whatever else you need here -->
</g:each>
</g:if>
</g:each>
</div>
EDIT - sql log below
Hibernate: select this_.id as id5_1_, this_.version as version5_1_, this_.date_created as date3_5_1_, this_.last_updated as last4_5_1_,
this_.portdescrip as portdesc5_5_1_, this_.portfolio_name as portfolio6_5_1_, this_.portpublished as portpubl7_5_1_, this_.profile_id as profile8_5_1_,
this_.status as status5_1_,
publicatio1_.portfolio_id as portfolio5_5_3_,
publicatio1_.id as id3_, publicatio1_.id as id2_0_,
publicatio1_.version as version2_0_,
publicatio1_.date_created as date3_2_0_,
publicatio1_.last_updated as last4_2_0_,
publicatio1_.portfolio_id as portfolio5_2_0_,
publicatio1_.publication_content as publicat6_2_0_,
publicatio1_.publication_name as publicat7_2_0_,
publicatio1_.published as published2_0_,
publicatio1_.publisheddate as publishe9_2_0_,
publicatio1_.publishedemail as publish10_2_0_,
publicatio1_.pubproduct_id as pubproduct11_2_0_
from portfolio this_ left outer join publication publicatio1_
on this_.id=publicatio1_.portfolio_id where (this_.status=?)
and (publicatio1_.published=?) order by publicatio1_.last_updated desc
You are getting the java.lang.ClassCastException because portfolios is a list and portfolio in the Publication class (probably) isn't, it's probably an id (long); can't cast a list in any meaningful way to compare to a long in eq ('portfolio', portfolios)
You should not need two separate queries since the domain classes are related.
--EDIT--
Editing to not use separate action and just use list action. I've not been able to get the include to work either, but below is pretty much what I've in dozens of cases. If there is some reason you can't do this then maybe a new question on just using the include mechanism might generate some attention.
I'm not sure what your current list action looks like. This is how I would code a list method to get ALL Portfolios and their last 5 Publications. No need for any parameters because I'm returning ALL portfolios.
//PortfolioController
def list (){
def portfolios = Portfolio.createCriteria().list {
//if you needed to filter the list by for example portfolio status or something you could add that here
or {
eq('status','ACTIVE')
eq('status','PENDING')
}
publications(org.hibernate.criterion.CriteriaSpecification.LEFT_JOIN) {
eq("published", "Yes")
order("lastUpdated", "desc")
firstResult(5)
}
}
[portfolios: portfolios, portfolioCount:portfolios.size()]
}
Now the Publications come pre-attached to their Portfolio.
The LEFT_JOIN part of the above insures you get back a list of portfolios with ONLY those Publications attached that meet the criteria; if you leave that out it defaults to an inner join and when you iterate you will get ALL of the Publications for that Portfolio (even if they don't meet the criteria).
Then in your gsp iterate through the portfolios - it can either be directly in the list.gsp or in a template rendered in list.gsp. If you put it in a template called _webList.gsp you would render it in the list.gsp as
<g:render template="weblist" model="['portfolios': portfolios]" />
This is either in list.gsp or _webList.gsp - I would start with it directly in list.gsp to make sure it's all working.
<g:each in="${portfolios}" var="portfolioInstance" status="i">
${portfolioInstance?.portfolioName?.encodeAsHTML()
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:each in="${portfolioInstance.publications"} var="publicationInstance" status="j">
${publicationInstance.id}
<!-- and again whatever else you need here -->
</g:each>
</g:each>
--EDIT--
firstResult(5) seems to do the trick.
--EDIT--
You'll notice I have the maxResults(5) commented in there - I'm having trouble getting that to work correctly. It seems to control the number of portfolios that are returned even though it is in the association block. Maybe someone else will see this and add that piece of the puzzle - or tinker with it yourself. I'll keep trying and update if I figure it out.

Resources