Show News Item creator or owner full name in Plone - plone

I'm trying to show the full name for each news item in a list. For the moment I have only the user id (nickname).
Is there a simple way (in existing .pt file) to show the full name of creator or owner instead of a nickname?
The page must work for anonymous users, too. I mean - the page must be public.
Some details:
<div class="container-fluid news-list-container"
tal:define="news_items python:context.getFolderContents(contentFilter={'portal_type':['News Item'], 'sort_on': 'Date', 'sort_order': 'descending',});
Batch python:modules['Products.CMFPlone'].Batch;
b_size python:4;
b_start python:0;
b_start request/b_start | b_start;
batch python:Batch(news_items, b_size, int(b_start), orphan=0);"
tal:condition="news_items">
<div class="news-list-items">
<tal:items tal:repeat="news_item batch">
<!-- News item -->
<div class="row news-item"
tal:define="news_object python:news_item.getObject();
news_date python:news_object.getField('modification_date').getAccessor(news_object)();
news_title python:news_object.getField('title').getAccessor(news_object)();
news_description python:news_object.getField('description').getAccessor(news_object)();
news_image python:news_object.getField('image').getAccessor(news_object)();
news_url python:news_object.absolute_url();
news_creators python:news_object.getField('creators').getAccessor(news_object)(); .... ...

<tal:fullname define="membership context/portal_membership;
info python:membership.getMemberInfo(user.getId());
fullname info/fullname">
You are are <span class="name" tal:content="fullname" />
</tal:fullname>
This example is taken from the plone documentation

You can get inspired a lot by this code:
https://github.com/collective/Products.Scrawl/blob/1021047c4ef6c2655d104e8b345a24140da9e4aa/Products/Scrawl/browser/blogentry_view.pt#L32
<tal:name tal:condition="item_creator"
tal:define="author python:context.portal_membership.getMemberInfo(item_creator)">
<span i18n:translate="label_by_author">Posted by
<a href="#"
title="Read more posts by this author"
tal:attributes="href string:${context/portal_url}/author/${item_creator}"
tal:content="python:author and author['fullname'] or item_creator"
tal:omit-tag="not:author"
i18n:domain="scrawl"
i18n:name="author"
i18n:attributes="title author_title">
Bob Dobalina
</a>
</span>
</tal:name>
Mind the possible performance issues.
A cached view method may work a lot better, e.g.:
#memoize
def userid2fullname(self, userid):
pm = api.portal.get_tool('portal_membership')
memberinfo = pm.getMemberInfo(userid)
return memberinfo and memberinfo['fullname'] or userid

Related

Custom Data Attributes HTML Nesting issues + Data Collection using GTM and GA4

I'm developing custom event tracking on my website using Custom Data Attributes (data-*) in the HTML code. The data collected should be sent to Google Analytics using Google Tag Manager (that enables the data collection using variables, triggers, and tags).
However, I'm facing several issues.
I have different sub-level data attribute categories. For example:
data-analytics-region (nesting the data attributes below)
data-analytics-section-engagement (nesting the data attributes below)
data-analytics-title (etc.)
Here is an example:
<section class="homepage-section collection-module" data-module-template="tiles" data-analytics-region="tiles">
<div data-unit-id="maintenance" data-analytics-section-engagement="maintenance">
<div class="unit-wrapper">
<a class="unit-link" href="/fr/services/maintenance/" target="_self" rel="follow" data-analytics-region="learn more"></a>
<div class="unit-copy-wrapper">
<h4 class="headline">Entretien</h4>
<h5 class="subhead" role="presentation">Moteur de performances<br class="large-hide medium-hide small-show" /> qui tiennent la route.</h5>
<div class="cta-links">
<a class="icon icon-after icon-chevronright" href="/fr/services/maintenance/" target="_self" rel="follow" data-analytics-title="Learn more - Maintenance" aria-label="En savoir plus sur l'entretien">En savoir plus</a>
<a class="icon icon-after icon-chevronright nowrap" href="/fr/contact/" target="_self" rel="follow" data-analytics-title="Free estimate - Maintenance" aria-label="Obtenez gratuitement un devis">Devis gratuit</a>
</div>
</div>
<div class="unit-image-wrapper">
<figure class="unit-image unit-image-maintenance-tile" data-progressive-image></figure>
</div>
</div>
</div>
<div data-unit-id="financing" data-analytics-section-engagement="financing">
<div class="unit-wrapper">
<a class="unit-link" href="/fr/services/financing/" target="_self" rel="follow" data-analytics-region="learn more"></a>
<div class="unit-copy-wrapper">
<h4 class="headline">Financement</h4>
<h5 class="subhead" role="presentation">Pour toutes les marques.<br class="large-hide medium-hide small-show" /> Et toutes les bourses.</h5>
<div class="cta-links">
<a class="icon icon-after icon-chevronright" href="/fr/services/financing/" target="_self" rel="follow" data-analytics-title="Learn more - Financing">En savoir plus</a>
</div>
</div>
<div class="unit-image-wrapper">
<figure class="unit-image unit-image-financing-tile" data-progressive-image></figure>
</div>
</div>
</div>
</section>
I was always able to retrieve the correct data-analytics-title value (which is the lowest data attribute category level in the above code). However, for the other data attributes, it failed.
Here is how I set up the data attributes data collection in Google Tag Manager:
For example, if I clicked on
<a class="icon icon-after icon-chevronright" href="/fr/services/maintenance/" target="_self" rel="follow" data-analytics-title="Learn more - Maintenance" aria-label="En savoir plus sur l'entretien">En savoir plus</a>
Here is what I get using the Google Tag Assistant (preview mode):
As you can see, only the data-analytics-title has a value.
I want the data-analytics-region and data-analytics-section-engagement to have a value as well (here it says undefined), so that I'm able to know where exactly the user clicked on the page. What am I doing wrong?
Finally, and it is linked to my 1st question, I want to be able to link the different data attributes so that I know from which page/section/link/etc. the user triggered an event. I'd like to have a clear view of that in my GA4 Reports. How can I do so?
Thanks for your help :-)
First thing we need to know is that user is clicking which element. And the Data you need is which element.
Let's take a simple version of the html code.
<x data-analytics-region="xxx">
<y data-analytics-section-engagement="yyy">
<z data-analytics-title="zzz">
</z>
</y>
</x>
Now the user is clicking the z element. So the GTM trigger fired and get the data-analytics-title
But we don't get the value from data-analytics-section-engagement and data-analytics-region. Why? Because they are different element
So we need to find those element first. Based on the click element.
From the example :
We need to find the y element and x element based on z element.
There are few ways we can do it.
I would try on the Element.closest()
Not test yet but similar to
var clickElement = {{Click Element}};
var findXElement = clickElement.closest('x');
var dataAnalyticsRegionValue = findXElement.getAttribute("data-analytics-region");
var findYElement = clickElement.closest('y');
var dataAnalyticsSectionEngagementValue = findYElement.getAttribute("data-analytics-section-engagement");
You might need to adjust the code based on your real html but the direction is the same.

How to associate a heading/document title with an accordion button

Obligatory 'I have googled but am stuck'. I have a number of documents on a page with associated collapsible 'More Info' accordions. I want to be able to report on when people click to open one of these accordions and what the corresponding document name is.
Implementing tracking in GTM to show opens on the accordions are not a problem, but how do I associate the document name (h4) with the click event?
Div containing document, document heading and associated 'More Info':
enter image description here
Div that the document heading and the more info button are in:
<div class="employee-comms-results-documents">
<div class="row">
<div class="col-md-9 col-sm-8 col-xs-12 no-padding-right no-padding-left">
<div>
<div class="primary-document-meta"><div class="doc-type-pill" id="doc-type-pill-Flyers">Flyers
</div>
</div>
<h4>Helping you save for retirement</h4>
<div class="secondary-document-meta">More info <span class="caret"></span>
</div>
</div>
</div>
Thanks in anticipation!
I think I would need to see the whole site structure to be sure, but for your example I created a JavaScript variable in GTM that returns the corresponding h4-text on click.
This code has worked for me:
function(){
var clickElement = jQuery({{Click Element}}).parent(".secondary-document-meta").parent("div").find("h4").text();
return clickElement;
}

Rich Snippets Nesting Issue

I've never used Rich Snippets before, so this is a little bit of a learning curve for me. I believe my issue is a nesting problem but I can't find any documentation anywhere that explicitly states how to nest these properties correctly.
I'm wanting to index a single-product review with multiple reviews into Rich Snippets with classic ASP pulling in different data feilds, here is my code:
<div>
<div itemscope itemtype="http://data-vocabulary.org/Review">
<span itemprop="itemreviewed">Forma Stanzol</span><br />
By <span itemprop="reviewer"><%=formaStanzolReviewArray(0,i)%></span><br />
<time itemprop="dtreviewed" datetime="<%=FormatDateTime(formaStanzolReviewArray(1,i),2)%>"><%=FormatDateTime(formaStanzolReviewArray(1,i),2)%></time> <br />
<span itemprop="description"><%=formaStanzolComment%></span>
</div>
</div>
This returns the Error: No rich snippet will be generated for this data, because it appears to include multiple reviews of an item, but no aggregate review information.
So, I added a dummy Aggregate code with static values, here's what it looks like all together:
<div>
<div itemscope itemtype="http://data-vocabulary.org/Review">
<span itemprop="itemreviewed">Forma Stanzol</span><br />
By <span itemprop="reviewer"><%=formaStanzolReviewArray(0,i)%></span><br />
<time itemprop="dtreviewed" datetime="<%=FormatDateTime(formaStanzolReviewArray(1,i),2)%>"><%=FormatDateTime(formaStanzolReviewArray(1,i),2)%></time> <br />
<span itemprop="description"><%=formaStanzolComment%></span>
</div>
<div itemscope itemtype="http://data-vocabulary.org/Review-aggregate">
<span itemprop="itemreviewed">Forma Stanzol</span>
<span itemprop="rating" itemscope itemtype="http://data-vocabulary.org/Rating">
<span itemprop="average">9</span>
out of <span itemprop="best">10</span>
</span>
based on<span itemprop="count">5</span> user reviews.
</div>
</div>
This causes my "Reviews" to not error but then all of my "Aggregate Reviews" push out this Error: No rich snippet will be generated for this data, because it appears to include multiple aggregate reviews of many items, instead of a single aggregate review of one item.
Seems like it's working against itself no matter what I do, so that's why I believe this to be a nesting issue.
How can I fix this?
EDIT: Ideally, I don't event want the Aggregate view of this item. The reviewer, item name, review date, and review description is all I need.
EDIT EDIT: This code is also running in a For loop where its getting information from the database with each pass.
Ok so the issue here was that a website, with a single product, but multiple reviews needs only one "Review-Aggregate" and one "Rating" itemtype. However, multiple "Review" itemtypes must be used.
So, my For Loop creates a "Review" for each row in the database, using the related data feilds and then after the conditional statement, the "Review-Aggregate" and Rating" codes are placed.
Code:
For i = 0 to uBound(formaStanzolReviewArray,2)
reviewCount = reviewCount + 1
formaStanzolComment = trim(formaStanzolReviewArray(2,i))
'Do not show reviews with empty comments
If Not (formaStanzolComment = "") OR isNull(formaStanzolComment) Then
%>
<div>
<div itemscope itemtype="http://data-vocabulary.org/Review">
<span style="position: absolute; left: 9999px;" itemprop="itemreviewed">Forma Stanzol</span>
Rating: <span itemprop="rating"><%=formaStanzolReviewArray(3,i)%></span> -
By <span itemprop="reviewer"><%=formaStanzolReviewArray(0,i)%></span> -
<time itemprop="dtreviewed" datetime="<%=FormatDateTime(formaStanzolReviewArray(1,i),2)%>"><%=FormatDateTime(formaStanzolReviewArray(1,i),2)%></time> <br />
<span itemprop="description"><%=formaStanzolComment%></span>
</div>
</div>
<%
sumRating = sumRating + formaStanzolReviewArray(3,i)
End If
Next
ratingAvg = sumRating / reviewCount
%>
<div style="position: absolute; left: 9999px;">
<div itemscope itemtype="http://data-vocabulary.org/Review-aggregate">
<span itemprop="rating" itemscope itemtype="http://data-vocabulary.org/Rating">
<span itemprop="worst">1</span>
<span itemprop="average"><%=ratingAvg%></span>
out of <span itemprop="best">5</span>
</span>
based on <span itemprop="votes"><%=reviewCount%></span> ratings.
<span itemprop="count"><%=reviewCount%></span> user reviews.
</div>
</div>
<%
Think of it as multiple User reviews in the For Loop, but we collect all of those reviews once in the aggregate, and then give that aggregate a rating scale.
Hope this helps anyone having nesting issues.
Please Note: I am using classic ASP for this particular code.

In NopCommerce I need to display the shopping cart outside of the the HeaderLinks partial, where should I add a new controller?

I need to display the shopping cart outside of the the HeaderLinks partial which takes the cart items out of the model passed by the CommonController.HeaderLinks action. By creating a new controller with an action that passes the cart info I was able to get the custom ui element from our template working. Right now the controller is inside a custom plugin I got going for some other stuff. The way I see it I have two options:
Leave the controller in the plugin project and live with the fact that if the plugin for some reason is not installed (i.e. a fresh checkout from a new dev.) the theme is going to break, possibly redirecting the user to the error view.
Put the controller in the Controllers folder at Nop.Web with the downside that this would add an extra step to the process of upgrading NopCommerce. If similar issues arise this could get ugly pretty quick.
So my question is: where is the best place to put the controller? Or is there a simpler way to do this thing with the shopping cart?
Regards,
Jose
i will show you a shortcut way to solve it. First create a partial (razor) view e.g _CustomPartialView and then add these usings at the top inside the partial view
#using Nop.Core;
#using Nop.Core.Infrastructure;
#using Nop.Services.Orders;
then next is to query the shopping cart which is easy becuase nopCommerce has already static way of accessing depency resolver method i.e EngineContext.Current.Resolve<T>(). In your case (for shopping cart) it could be the following;
#{
var shoppingCartEnabled = EngineContext.Current.Resolve<Nop.Services.Security.IPermissionService>()
.Authorize(Nop.Services.Security.StandardPermissionProvider.EnableShoppingCart);
var customer = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer;
int shoppingCartItems = 0;
if (customer.HasShoppingCartItems)
{
shoppingCartItems = customer.ShoppingCartItems
.Where(sci => sci.ShoppingCartType == Nop.Core.Domain.Orders.ShoppingCartType.ShoppingCart)
.LimitPerStore(EngineContext.Current.Resolve<IStoreContext>().CurrentStore.Id)
.ToList()
.GetTotalProducts();
}
if (shoppingCartEnabled)
{
<div class="header-right pull-right wrap-cart hidden-xs ">
<div class="cart-top pull-right">
<div id="cart">
<span class="icon fa fa-shopping-cart"></span>
<div class="heading">
<a href="#Url.RouteUrl("ShoppingCart")" class="ico-cart dropdown-toggle visible-md visible-lg" data-toggle="dropdown" data-hover="dropdown">
<h4 class="cart-label">
#T("ShoppingCart")
<span>#T("ShoppingCart.HeaderQuantity", shoppingCartItems)</span>
</h4>
</a>
</div>
<div class="content">
#if (!String.IsNullOrWhiteSpace(Html.Action("FlyoutShoppingCart", "ShoppingCart").ToString()))
{
<li>#Html.Action("FlyoutShoppingCart", "ShoppingCart")</li>
}
</div>
</div>
</div>
</div>
}
}
Let me know if you need more help :)

How to hide navigation portlet after logout?

How to hide a navigation portlet after logging out? What I exactly want is, if a logged-in admin visits a site, the navigation portlet should appear and be visible to him until he logs out from the plone site. But if a normal user visits the plone site, the portlet should not appear.
A nice feature of Plone is the option to assign portlets not only to locations, but also to contenttypes and groups. To solve your request, simply assign the portlet to the group 'Administrators'.
To do that, go to your Plonesite's controlpanel, click 'Users and Groups', click on green tab 'Groups', click on 'Administrators', click on the tab 'Group portlets', add a portlet.
To reproduce this programtically, use Genericsetup (export portlets.xml of the site and include the relevant parts in your product).
For completeness: Similiar for assigning portlets to a contenttype, you go to the controlpanel, click 'Types', choose the wanted type (f.e. 'Event') of the dropdownlist and click on 'Manage portlets assigned to this content type', assign portlet. Progragramtically reproducable via exporting 'Types' and including it to your products.
Thank you all, for all the response towards my question. Unfortunately the solution you all provided doesn't work for me. So I myself Did like this to hide a navigation portlet when logged out.
Step 1: In overrides.zcml
<plone:portlet
name="navigation_bar"
interface="plone.app.portlets.portlets.navigation.INavigationPortlet"
assignment="plone.app.portlets.portlets.navigation.Assignment"
renderer=".browser.navi_portlet.navigation_portlet"
addview="plone.app.portlets.portlets.navigation.AddForm"
editview="plone.app.portlets.portlets.navigation.EditForm"
/>
<plone:portletRenderer
portlet="plone.app.portlets.portlets.navigation.INavigationPortlet"
class=".browser.navi_portlet.navigation_portlet"
layer=".interfaces.IThemeSpecific"
/>
renderer=".browser.navi_portlet.navigation_portlet" here browser is my folder which contains a file called navi_portlet with a method navigation_portlet.
step 2: navi_portlet.py:
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.CMFPlone import PloneMessageFactory as _
from plone.app.portlets.portlets.navigation import Renderer
from plone.app.layout.viewlets.common import PersonalBarViewlet
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from plone.app.layout.globals.interfaces import IViewView
class navigation_portlet(Renderer,PersonalBarViewlet):
_template= ViewPageTemplateFile('templates/nav_portlet.pt')
def nav_up(self):
mytal = PersonalBarViewlet.update(self)
what i did in navi_portlet.py is i just inherited the PersonalBarViewlet from viewlets(eggs folder) and Renderer from portlets(eggs folder). To override the default behavior to hide it when logged out.
step 3: nav_portlet.pt
<dl class="actionMenu deactivated" id="portlet portletNavigationTree"
tal:define="user_actions python:view.nav_up();root view/navigation_root"
tal:condition="python:view.user_actions and not view.anonymous">
<tal:block condition="not: view/anonymous">
<dt class="portletHeader"
tal:attributes="class python:view.hasName() and 'portletHeader' or 'portletHeader hiddenStructure'">
<span class="portletTopLeft"></span>
<a href="#"
class="tile"
tal:attributes="href string:${view/heading_link_target}"
tal:content="view/title"
i18n:translate="">Navigation</a>
<span class="portletTopRight"></span>
</dt>
<dd class="portletItem lastItem">
<ul class="navTree navTreeLevel0">
<li tal:define="selectedClass view/root_item_class;
li_class python:selectedClass and ' navTreeCurrentNode' or '';
normalizeString nocall:context/plone_utils/normalizeString;
section_title root/Title;
section python:normalizeString(section_title);"
tal:condition="view/include_top"
tal:attributes="class string:navTreeItem navTreeTopNode${li_class} section-${section}">
<div tal:define="rootIsPortal view/root_is_portal;
root_icon view/root_icon;
root_type root/portal_type;
root_type_class python:'contenttype-' + normalizeString(root_type);
root_class python:rootIsPortal and 'contenttype-plone-site' or root_type_class;">
<a tal:attributes="href root/absolute_url;
title root/Description;
class python:' '.join([root_class, selectedClass]).strip();">
<img tal:replace="structure root_icon/html_tag" tal:condition="not:rootIsPortal" />
<span tal:omit-tag=""
tal:condition="rootIsPortal"
i18n:translate="tabs_home">Home</span>
<span tal:condition="not:rootIsPortal"
tal:replace="root/Title">Root item title</span>
</a>
</div>
</li>
<li tal:replace="structure view/createNavTree">
SUBTREE
</li>
</ul>
<span class="portletBottomLeft"></span>
<span class="portletBottomRight"></span>
</dd>
What i tried to do in nav_portlet.pt is to merge both portlets(Navigation Portlet using Render(class)) and viewlets(PersonalBarViewlet). so that i used the user_action method from the PersonalBarViewlet Class (i.etal:condition="python:view.user_actions and not view.anonymous">) to hide the Navigation Portlet when logged out.
Hope You all get my points and what i did.
Thanks

Resources