Public
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Mentor

Show most recent activity in feeds

Hey guys- 

1) is there a way to make it so our community displays topics based on latest activity? So, obviously new topics are shown at the top of the list, but currently if a user comments on something from a day ago or the past, it doesn't bubble up to the top. It remains in the order of which it was posted. Any idea how to change this?

2) if it isn't possible, does anyone know the reason why the current way might be better? 

Thanks in advance!

 

20 Replies 20
Moderator

Hi @akloepfer! You can select the formats and sorting order for displaying posts and their replies. Set topic and post display options might help.

I reviewed this content, but maybe i'm overlooking the specific info i'm looking for.  I'm looking for a setting that will help me make the feed more dynamic based on recent activity. For example, right now our feed shows the most recent main topic posted. So If I comment on a thread from monday, it'll stay where it is...lower in the feed, it won't pop up to the top of the feed. I'd like to fix this to work as anything with any form of activity will pop up to the top of the feed, 

@akloepfer Thank you for reviewing. As the topics that stay at the top are not floating or featuring, I'd recommend trying some workaround by following Feature a topic on the community, and Why are the topics on a board out of order?.

Please let me know if the issue persists, so I can help create a support ticket to investigate this further.

Mentor

Based on the articles you shared and how our community is working i think i may need to open a support ticket. 

 

Thanks!

@akloepfer Appreciate you going through the articles and following the workaround. I have passed on your query to our technical support. They will investigate this and help you out.

Thank you for your patience!

Director

Hi @akloepfer,

In our Community, we use both types of filters: based on the creation date and latest activity (really the latest one). Fresh comments always push the topics to the top of the activity feed in such a situation. 

We also added more filters for ourselves, for example, a filter that displays unanswered questions:

filters.png

Karolina

Nice work @karolinalinda. I would love to be able to have some data on how often users change these kind of default settings. Or for example, if introducing a filter for showing unanswered questions increased the amount of answers shared by the community or helped drive down the average response times.  

 

@akloepfer - I hope you solved your issue. There's definitely lots of value in ensuring that older topics can again bubble to the top. I manage a home improvement community and topics don't typically age at all. So a discussion from years ago can still be really valuable and you certainly want it to appear again at the top of the feed if someone has added a new question or piece of advice. 

 

Jason

 

Generic1.jpg

Director

Introducing the Unanswered questions filter helps a lot in reducing the number of zeros in open Q&A forums 🙂 We have some Community members fighting for badges related to replies or solutions and they love the ease of filtering out the unanswered questions 😃

Also, for the second time, we run a "cleanup" challenge in our Community, asking our users to address those still unanswered questions: https://community.dynatrace.com/t5/Community-challenges/Take-the-Forum-Hero-Challenge/m-p/188413 🙂

Karolina

Hi @karolinalinda, do you have any advice on how to customize or add filters?

Director

Adding our Studio guru here, @MaciejNeu 😉

Karolina
Mentor

Hi @matthew_cowan 

If you have an access to your message list component in Studio (we have a customized one), then you should see there how the message list is sorted.

Usually, it is something like:

<assign dataForMessageList = takeData + orderIt + andLimitIt>

takeData will be the "take this and that data from this part". For example:

<#assign takeData = "SELECT id, subject, body, metrics.views, tags, labels, images, post_time, post_time_friendly, conversation.last_post_time, moderation_status, conversation.last_post_time_friendly, author.login, author.avatar.message, view_href, author.view_href, read_only, author.rank.name, author.rank.color, author.rank.icon_left, author.rank.bold, author.rank.icon_right, author.online_status,board.id, author.avatar.profile, board.view_href, board.title, replies.count(*), kudos.sum(weight), conversation.solved, user_context.read FROM messages WHERE depth = 0 AND board.id = '${coreNode.id}' " />

 
"orderIt" is the part you are looking for, for the filtering. It can look something like this:

<#switch sorting>
   <#case "kudos">
       <#assign orderIt = "ORDER BY kudos.sum(weight) DESC " />
   <#break>
   <#case "views">
       <#assign orderIt = "ORDER BY metrics.views DESC " />
   <#break>
   <#case "replies">
       <#assign orderIt = "ORDER BY replies.count(*) DESC " />
   <#break>
   <#case "post_time">
       <#assign orderIt = "ORDER BY post_time DESC " />
   <#break>
   <#case "unaswered">
       <#assign orderIt = "AND replies.count(*)=0 ORDER BY post_time DESC " />
   <#break>
   <#case "no_solution">
       <#assign orderIt = "AND conversation.solved=false ORDER BY post_time DESC " />
   <#break>
   <#default>
       <#assign orderIt = "ORDER BY conversation.last_post_time DESC " />
   <#break>
</#switch>

 

So adding three new filtering modes (post_time, no_solution, and unanswered) was for us just the case of adding the different logics of sorting data to the code and then later adding it to the dropdown menu.

What kind of sorting you'll use is all up to you. Our users were looking for an easy way of finding questions without answers or accepted solutions, so this was important for us to make their experience of helping others in the forum as easy as possible.

<select id="community-activity-sorted-by">
                   <option value="recent" <#if sorting == 'recent'>selected</#if>>${text.format("theme-lib.community-activity.recent")}</option>
                   <option value="views" <#if sorting == 'views'>selected</#if>>${text.format("theme-lib.community-activity.views")}</option>
                   <option value="replies" <#if sorting == 'replies'>selected</#if>>${text.format("theme-lib.community-activity.replies")}</option>
                   <option value="kudos" <#if sorting == 'kudos'>selected</#if>>${text.format("theme-lib.community-activity.kudos")}</option>
                   <option value="post_time" <#if sorting == 'post_time'>selected</#if>>${text.format("New questions")}</option>
                   <option value="unaswered" <#if sorting == 'unaswered'>selected</#if>>${text.format("Unanswered questions")}</option>
                   <option value="no_solution" <#if sorting == 'no_solution'>selected</#if>>${text.format("Without solution")}</option>
               </select>

 

Thank you so much! I'll check this out today, I was working on this yesterday and honed in on this area, but was applying the wrong data filters. I appreciate your quick response!

Hello @MaciejNeu,

 

I have added the codes as you proposed and in the preview of the component I see them but in the community they are not showing... Is there anything else I should look at?

This is such a helpful guide to customizing the options in the message list. I was able to access the code for this component in our instance and I notice that we don't have orderIt section that you mentioned. This means that our 'Most Recent' sort option is only sorting on date of post published but not including the most recent activity to push topics up the feed when they get replies. 

Can you let me know where in the code the orderIt section should be placed so I can add in the code you have shared in the right place?

Senior Community Manager | Strava
https://communityhub.strava.com
Khoros Staff

Hello @elbranscomb 
You need to paste the code in the same component you have query for pulling messages content and then update your query like below.

like pull data for messages like below:

 

<#assign baseQry = "SELECT id, subject, body, metrics.views, tags, labels, images, post_time, post_time_friendly, conversation.last_post_time, moderation_status, conversation.last_post_time_friendly, author.login, author.avatar.message, view_href, author.view_href, read_only, author.rank.name, author.rank.color, author.rank.icon_left, author.rank.bold, author.rank.icon_right, author.online_status,board.id, author.avatar.profile, board.view_href, board.title, replies.count(*), kudos.sum(weight), conversation.solved, user_context.read FROM messages WHERE depth = 0 AND board.id = '${coreNode.id}' " />

 

  Now add the following code to the same component below above code:

<#switch sorting>
   <#case "kudos">
       <#assign orderIt = "ORDER BY kudos.sum(weight) DESC " />
   <#break>
   <#case "views">
       <#assign orderIt = "ORDER BY metrics.views DESC " />
   <#break>
   <#case "replies">
       <#assign orderIt = "ORDER BY replies.count(*) DESC " />
   <#break>
   <#case "post_time">
       <#assign orderIt = "ORDER BY post_time DESC " />
   <#break>
   <#case "unaswered">
       <#assign orderIt = "AND replies.count(*)=0 ORDER BY post_time DESC " />
   <#break>
   <#case "no_solution">
       <#assign orderIt = "AND conversation.solved=false ORDER BY post_time DESC " />
   <#break>
   <#default>
       <#assign orderIt = "ORDER BY conversation.last_post_time DESC " />
   <#break>
</#switch>

 Now add the following line below this:

<#assign qry = baseQry + orderIt />

 Now execute the above LiQL like below:

<#assign topics = executeLiQLQuery(qry) />

 
Now the above query will execute whichever case you selected from UI and pass to switch case as a sorting parameter. Later it will pull the data in required format and display.

Regards,
Abhishek Gupta

Thanks @AbhishekGu for providing the details to accomplish this. I tried to integrate the code into the OOB component code and it keeps throwing a Freemarker error.

Here's the code I have

<#-- custom community activity -->
<#import "theme-lib.common-functions.ftl" as commonUtils />
<#import "theme-lib.community-activity-macros.ftl" as messageUtils />
 
<@compress single_line=true>
   <#assign pageSize = settings.name.get("layout.messages_per_page_linear", "5")?number />
   <#assign scope = (env.context.component.getParameter('scope'))!"all" />
   <#assign allowedInteractionStyles = coreNode.settings.name.get("custom.allowed_interaction_styles", page.interactionStyle) />
   <#assign sumThreadKudosCount = coreNode.settings.name.get("layout.sum_thread_kudos", "false") />
   <#assign loadMoreData = {"messageListType": "recent", "currentPage": "0"} />
   <#assign loadMoreObject = http.session.attributes.name.get("${coreNode.id}Loader", utils.json.toJson(loadMoreData)) />
   <#attempt>
       <#assign loadMoreObject = utils.json.fromJson(loadMoreObject) />
   <#recover>
       <#assign loadMoreObject = loadMoreData />
   </#attempt>
   <#assign messageListType = loadMoreObject.messageListType!"recent" />
   <#assign currentPage = (loadMoreObject.currentPage!"0")?number />
   <#assign nodeId = coreNode.id />
   <#assign initialPayload = (currentPage + 1) * pageSize />
   <#assign messagesCount = messageUtils.getMessagesCount(nodeId, messageListType, scope, allowedInteractionStyles) />
   <#assign messages = messageUtils.getLazyLoadMessages(nodeId, messageListType, initialPayload, scope, 0, allowedInteractionStyles) />
 
   <#assign timezone =restadmin("users/id/${user.id}/settings/name/config.timezone").value />

   <#if messages?size gt -1>
       <div class="custom-community-activity" id="custom-loader">
           <section>
               <header>
                   <h2>${text.format("theme-lib.community-activity.title", coreNode.title)}</h2>
                   <div>
                       <label for="community-activity-sorted-by">${text.format("sortingbar.sortby")}</label>
                       <select id="community-activity-sorted-by">
                           <option value="recent" <#if messageListType == "recent">selected</#if>>${text.format("theme-lib.community-activity.recent")}</option>
                           <option value="views" <#if messageListType == "views">selected</#if>>${text.format("theme-lib.community-activity.views")}</option>
                           <option value="replies" <#if messageListType == "replies">selected</#if>>${text.format("theme-lib.community-activity.replies")}</option>
                           <option value="topkudos" <#if messageListType == "topkudos">selected</#if>>${text.format("theme-lib.community-activity.kudos")}</option>
                       </select>
                       <@component id="theme-lib.start-conversation-button" />
                   </div>
               </header>
               <section id="custom-loader-messages">
                   <div class="errors"></div>
                   <div class="message-list" data-attrib-current-page="${loadMoreObject.currentPage}" data-attrib-message-list-type="${loadMoreObject.messageListType}">
                       <#setting time_zone = timezone>
                       <#list messages as msg>
                           <@messageUtils.printMsg msg msg?index messages?size false sumThreadKudosCount/>
                       </#list>
                   </div>
                   <div class="lia-view-all">
                       <a class="lia-link-navigation load-more-button <#if messagesCount lte pageSize>disabled</#if>" href="javascript&colon;;" id="custom-loader-button">${text.format("pager.paging.type.view-more.text")}</a>
                   </div>                  
               </section>
           </section>
       </div>
       <@liaAddScript>
       ;(function($) {
           $(document).ready(function() {
 
               var getMessages = function (spinnerTarget, clearMessages) {
                   var parentComponent = $('#custom-loader');
                   var messageTarget = $('.message-list', parentComponent);
                   var currentPage = null;
                   try {
                       currentPage = parseInt($(messageTarget).attr('data-attrib-current-page'), 10);
                   } catch (e) {
                       currentPage = 0;
                   }
                   
                   $.ajax({
                       type: 'post',
                       url : '${commonUtils.getEndpointUrl("theme-lib.community-activity")}',
                       dataType: 'json',
                       data: {"currentPage": currentPage, "node": "${coreNode.id}", "scope":"${scope}", "messageListType": $(messageTarget).attr('data-attrib-message-list-type'), "allowedInteractionStyles": "${allowedInteractionStyles}"},
                       context: parentComponent,
                       beforeSend: function(jqXHR, settings) {
                           <#-- add custom pre-request logic here -->
                           $('#custom-loader-messages .errors', parentComponent).empty();
                           $(spinnerTarget).prepend('<div class="spinner"></div>');
                       },
                       error: function (jqXHR, textStatus, errorThrown) {
                           $('#custom-loader-messages .errors', parentComponent).append(errorThrown);
                       },
                       success: function (data, textStatus, jqXHR) {
                           if (data.status == '${STATUS_SUCCESS}') {
                               if (clearMessages) {
                                   messageTarget.empty();
                               }
                               if (data.messages.length > 0) {
                                   messageTarget.append(data.messages);
                               } else {
                                   if (clearMessages) {
                                       messageTarget.append('<div class="no-messages">${text.format("theme-lib.community-activity.no-messages")}</div>');
                                   }
                               }
                               if (data.EOR == 'true') {
                                   $('#custom-loader-button').addClass('disabled');
                               } else {
                                   $('#custom-loader-button').removeClass('disabled');
                               }
                           } else {
                               $('#custom-loader-messages .errors', parentComponent).append(data.message);
                           }
                       },
                       complete: function(jqXHR, textStatus) {
                           $('.spinner', spinnerTarget).remove();
                       }
                   });
               };
 
               $('#community-activity-sorted-by').change(function() {
                   <#-- first, clear out the current messages, then make ajax request with new list type. -->
                   var messageTarget = $('#custom-loader .message-list');
                   messageTarget.attr('data-attrib-message-list-type', $(this).val());
                   $(messageTarget).attr('data-attrib-current-page', '0');
                   getMessages($('#custom-loader'), true);
               });
 
               $('#custom-loader-button').click( function(evt) {
                   evt.preventDefault();
                   if ($(this).hasClass('disabled')) {
                       return;
                   }
                   var currentPage = null;
                   var messageTarget = $('#custom-loader .message-list');
                   try {
                       currentPage = parseInt($(messageTarget).attr('data-attrib-current-page'), 10);
                   } catch (e) {
                       currentPage = 0;
                   }
                   $(messageTarget).attr('data-attrib-current-page', (currentPage + 1));
                   getMessages($('#custom-loader-button'), false);
               });
           });
       })(LITHIUM.jQuery);
       </@liaAddScript>
   </#if>
</@compress>
<#--/custom community activity -->

 

I tried to integrate your suggested code after the final <#assign timezone> section but that didn't work. 

I have to admit my knowledge in this area is limited so any additional advice you have is greatly appreciated so I can integrate the additional info to the right place in the code shared above. 

Thanks

Senior Community Manager | Strava
https://communityhub.strava.com
Khoros Staff

Hey @elbranscomb ,

theme-lib.community-activity-macros.ftl is used as messageUtils in above component that handles the ordering of posts as 
<#assign messages = messageUtils.getLazyLoadMessages(nodeId, messageListType, initialPayload, scope, 0, allowedInteractionStyles) />

 

It uses messageUtils.getLazyLoadMessages to fetch the messages if you go into theme-lib.community-activity-macros.ftl you will be able to see getLazyLoadMessages which handles order.

If you select recent from the dropdown, based on its query it will sort the post by conversation post time using the line below:

<#local orderClause = " ORDER BY conversation.last_post_time DESC LIMIT " + pageSize + " offset " + offset/>

Thanks for the pointer on that, I was able to find that component and the section on lazyloadmessages as below.

My objective is to change the dropdown sort options on the message list for the Home page, Category Page and Board Pages so that in addition to the current option we have named 'Most Recent' sort which uses post_time there should be an option to sort by 'Most Recent Reply' which I believe should use the last_post_time. 

The chronological view by post date means that posts that happened in the past and are no longer at the top of the feed are not easily findable/visible to viewers especially when they have a lot of activity. 

What's the best way to add an additional sort option to allow sorting by 'Most Recent Reply'?

<#-- lazy load messages -->
<#function getLazyLoadMessages nodeId messageListType pageSize scope offset allowedInteractionStyles="">
 <#local selectFields = "id, tags, labels, conversation.solved, conversation.last_post_time, conversation.last_post_time_friendly, user_context.read, images, board.id, board.title, board.view_href, teaser, body, post_time_friendly, subject, author.login, author.view_href, author.avatar.profile, author.rank.name, author.rank.color, author.rank.bold, post_time, view_href, kudos.sum(weight), replies.count(*), metrics.views, conversation.solved" />
 
 <#local conversationClause = messageUtils.getConversationClause(allowedInteractionStyles) />
 <#local where = " WHERE depth=0 " +  conversationClause />
 
 <#if scope == "category">
     <#local where = where + " AND category.id='${nodeId}' " />
     <#local orderLastPost = (restadmin("/categories/id/${nodeId}/settings/name/layout.sort_view_by_last_post_date").value)!"false" />
 <#elseif scope == "board">
     <#local where = where + " AND board.id='${nodeId}' " />
     <#local orderLastPost = (restadmin("/boards/id/${nodeId}/settings/name/layout.sort_view_by_last_post_date").value)!"false" />
 <#else>
     <#local orderLastPost = settings.name.get("layout.sort_view_by_last_post_date", "false") />
 </#if>
 
 <#if messageListType == 'recent'>
   <#if orderLastPost == "true">
       <#local orderClause = " ORDER BY conversation.last_post_time DESC LIMIT " + pageSize + " offset " + offset/>
   <#else>
       <#local orderClause = " ORDER BY post_time DESC LIMIT " + pageSize + " offset " + offset />
   </#if>
 <#elseif messageListType == "topkudos">
     <#local orderClause = " ORDER BY kudos.sum(weight) DESC LIMIT " + pageSize + " offset " + offset />
 <#elseif messageListType == "views">
     <#local orderClause = " ORDER BY metrics.views DESC LIMIT " + pageSize + " offset " + offset />
 <#elseif messageListType == "replies">
     <#local orderClause = " ORDER BY replies.count(*)  DESC LIMIT " + pageSize + " offset " + offset />
 <#else>
     <#local orderClause = "" />
 </#if>
 
 <#local msgQry = "SELECT ${selectFields} FROM messages ${where} ${orderClause}" />
 <#local msgRes = commonUtils.executeLiQLQuery(msgQry) />
 <#return msgRes />
</#function>
<#--/lazy load messages -->

 

Senior Community Manager | Strava
https://communityhub.strava.com
Champion

I was finally able to crack the reason why our most recent activity in our community activity wasn't working as expected. We have 4 options in our activity feed, one of which is Most Recent. Up until my discovery Most Recent meant it showed posts in chronological order by date of original post newest > oldest. 

However, when i checked in admin I noticed that under the tab Discussion Styles/  Posts & Topics that the setting that was being used was Original posting date. I have now switched this to Time of most recent post and it fixed the issue. @MohammedF mentioned that in his reply above and this was really helpful. 

Now our feed displays showing newest > oldest based on last activity on the topic whether that is a reply or is the original posting date. We now see topics created several weeks ago that are getting a lot of activity now and are popping up to the top of the list. This is exactly what our members have been asking for. 

My next plan is to add an additional sort option for Newest by OP date so we get both ordered by newest added topic and newest by latest activity. A lot of the info shared here has given me some good information to progress that effort. Thanks to everyone for the help. 

Senior Community Manager | Strava
https://communityhub.strava.com

@elbranscomb Glad that you were able to crack the reason and fix the issue, and thank you for letting us know how you were able to fix it. 

Welcome to the Technology board!

Curious about our platform? Looking to connect on social technology? You've come to the right place!

Are you a Khoros customer? For direct assistance from our Support team, please visit the Support Forum.