Liferay: setting portlet preferences from portlet controller - liferay

I want to ask you about one problem. I using Liferay 6.2. I have a custom portlet, which is embeded in my custom theme
$theme.runtime("mycustomportlet_WAR_mycustomportlet")
Here some settings from Liferay-portlet.xml
<preferences-unique-per-layout>false</preferences-unique-per-layout>
<preferences-owned-by-group>true</preferences-owned-by-group>
<instanceable>false</instanceable>
In portlet controller render method I set portlet preferences (I want to make my portlet borderless)
PortletPreferences preferences = renderRequest.getPreferences();
preferences.setValue("portletSetupShowBorders","false");
try {
preferences.store();
} catch (ValidatorException e) {
e.printStackTrace();
}
But when I check Liferay and my custom embeded portlet borders there are. But PORTLETPREFERENCE table has a row with preferences below, with ID's according to liferay-portlet.xml setting:
<portlet-preferences>
<preference>
<name>portletSetupShowBorders</name>
<value>false</value>
</preference>
</portlet-preferences>
Can someone explain me where is mistake? Seem's like Liferay doesn't understand. I know how to set preferences another way. But I need this way to be solved.

The problem is your liferay-portlet.xml:
<preferences-owned-by-group>true</preferences-owned-by-group>
The preferences for layout setup (including portletSetupShowBorders) is read from the layout, as long as it is not instanceable:
public PortletPreferences getStrictLayoutPortletSetup(Layout layout, String portletId) {
long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
if (PortletConstants.hasUserId(portletId)) {
ownerId = PortletConstants.getUserId(portletId);
ownerType = PortletKeys.PREFS_OWNER_TYPE_USER;
}
...
So as long as your preferences are stored per group, they are not taken into account....

Related

Inherited Kentico MVC Widget not registering in the CMS Admin site

I am registering a custom kentico widget.
When it was created, I could not locate it in the collection of other widgets on the page tab in the admin website of the CMS solution.
I also looked for my widget in the CMS_Widget Table with no luck. Is there another table which might house custom widgets?
My Controller
using System.Web.Mvc;
using Website.Contracts;
using Company.Views.FeaturedProduct;
[assembly: RegisterWidget(OptInOptOutWidgetController.Identifier, typeof(OptInOptOutWidgetController), "Opt In Opt Out List", Description = "Lists all products that are opted in", IconClass = "icon-bullseye")]
namespace Website.Controllers.Widgets
{
public class OptInOptOutWidgetController : WidgetController
{
public const string Identifier = "Website.Controllers.Widgets.OptInOptOutWidgetController";
public IApplyLoanService ApplyLoanService { get; }
public OptInOptOutWidgetController( IApplyLoanService _ApplyLoanService)
{
ApplyLoanService = _ApplyLoanService;
}
public ActionResult Index()
{
List<StateInfo> stateInfoList = ApplyLoanService.GetStateDetails();
List<SelectListItem> statesList = new List<SelectListItem>();
OptInOptOutWidgetViewModel viewModel = new OptInOptOutWidgetViewModel();
viewModel.StateList = stateInfoList.Where(x => x.StateName != null).Select(x => new OptInOptOutStateViewModel
{
StateName = x.StateName
}).ToList();
ViewBag.States = viewModel.StateList;
return PartialView("Widgets/OptInOptOut/_OptInOptOutProductList", viewModel);
}
}
}
My View
#using Company.Models.Widgets
#model Company.Models.Widgets.OptInOptOut.OptInOptOutWidgetViewModel
#if (Context.Kentico().PageBuilder().EditMode && Context.Kentico().PageBuilder().Initialized())
{
#Html.Kentico().PageBuilderScripts()
}
<div>
#if (Model.StateList != null)
{
<p>Select State</p>
<select>
#Html.DropDownList("States", new SelectList(Model.StateList), "")
</select>
}
else
{
<p>There are no states</p>
}
</div>
I referenced this post:
Unable to Create New MVC Widget in Kentico 12
I started making updates the code base but still nothing:
The AssemblyDiscoverable was already where it was supposed to be
We have other widgets published to the CMS but there is no clear path on how to reproduce. Are there any suggestions?
The CMS_Widget table is used for Portal Engine Widgets, which are very different from MVC Page Builder Widgets.
Portal Engine Widgets are used to create the Administration UI which is still built on ASP.NET Web Forms technology.
MVC Page Builder Widgets are not used anywhere in the Administration UI, only on the Live Site.
There is no database table that records all Widgets (or any Page Builder components) defined in the Live Site. Instead, these are all discovered at runtime by the Xperience framework based on your [assembly: RegisterX(...)] component registration attributes.
Your Widget will appear in the Page Builder UI in a dialog that opens when you click the + button in a Widget Zone.
If your Widget is registered in the MVC Live Site application, you do not need the AssemblyDiscoverable attribute - that is only required when your Xperience components are in separate class libraries. AssemblyDiscoverable tells Xperience to scan the class library assembly for components - without it, scanning that assembly is skipped.

Is this a bug in the Tab Container?

I think this is a bug in the Tab Container...
Opening a new tab using Java or the Server Side JavaScript createTab method when the new tab contains a panel that is set to be an iframe pointing to another XPage in the same database will always cause the XPage to get reloaded after loading about five or six tabs (in Chrome, IE does the same but it takes more tabs...)
If the tab contains an iframe that points to another database that holds the XPage it works fine.
The SSJS code is:
getComponent("djTabContainer1").createTab({title:"New tab"});;
The Java code is
public static boolean createTab(UIDojoTabContainer tabContainer) {
try {
if (tabContainer == null) {
tabContainer = (UIDojoTabContainer) Utils.findComponent("TabContainer");
if (tabContainer == null) {
return false;
}
}
String tabTitle = null;
String url = null;
String unid = null;
UIDojoTabPane newTab = null;
// get default number from current project preferences
tabTitle = "My Tabpage";
url = Utils.GetXpageURL("tabpage.xsp");
// create a new Tab
newTab = new UIDojoTabPane();
newTab.setTitle(tabTitle);
newTab.setTabUniqueKey(new Random().toString());
newTab.setClosable(true);
newTab.setId("TabContainer_" + unid);
newTab.setStyleClass("myTabContainer");
Utils.WriteToConsole("setting style class on " + newTab.getTitle());
// newTab.setStyle("height:auto;width:auto; overflow-y: auto;border: 0px;");
// create new Panel
UIPanelEx newPanel = new UIPanelEx();
newPanel.setId("IFrame" + unid);
newPanel.setStyleClass("iframeClass");
// make an iFrame of this panel with our URL as src
newPanel.setTagName("iframe");
Attr property = new Attr();
property.setName("src");
property.setValue(url);
newPanel.addAttr(property);
// add Panel to our new Tab
newTab.getChildren().add(newPanel);
// add the new tab to our tab container
tabContainer.getChildren().add(newTab);
tabContainer.setSelectedTab(unid);
return true;
} catch (Exception ex) {
Utils.WriteToConsole("Unable to add a new Tab Page to the Tab Container (com.tlcc.Main.createTab)", ex);
return false;
}
}
The XPage that is referenced in the src property of the iframe is very basic...
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button
value="Label"
id="button1">
</xp:button>
<xp:inputText id="inputText1"></xp:inputText></xp:view>
When the XPage reloads it has no more tabs (except the first tab that was created in the XPage at the start) and is not responsive.
Howard
It could be a server page persistence/component tree limit problem. If you use the default server page persistence, the server only stores 4 pages in memory (per user).
When you create new tabs on the page that loads another XPage, the server is filling up the server page persistence queue and when you hit the 5th tab, the current page is no longer part of the server page persistence (the component tree is no longer in memory) leading to a reload of the current page.
You can increase the number of pages stored (and also move the disk persistence instead of memory persistence). You can also set viewState to "nostate" on the XPage that you load in the iframe (at least to test out my theory).
See Toby Samples blog post on state and this answer on a similar state issue: https://stackoverflow.com/a/31431917/785061
So, to finalize this... the setting for viewstate=nostate allows more tabs but then does not keep the components in memory. So, it works well for readonly XPages but not when the document on the XPage is in edit mode. The setting Maximum Pages on disk in the Xsp properties on the persistence tab will allow more tabs with iframes that have XPages but still will crash at some point.
Net is, don't use multiple iframes that pull in XPages from the same nsf...

Craeting Custom Widget in Orchard 1.7

I am new to Orchard. So please forgive me if there is anything looking silly!
I want to create a custom widget for my Orchard website to encourage visitors to sign up for my Newsletter service. I have seen there is an option of using HTML widget but I want to create a new widget type like "Newsletter" which I shall use conditionally at AsideFirst block.
Is this possible to do? I only want to grab visitor's Name and Email address, and the form submission will be done using an action controller.
Do I have to create this widget through by-hand coding in VS? In fact I want to this way, not through the Orchard admin console.
Seeking for help. Any suggestion please?
Edit:
I have managed to create the widget following Sipke Schoorstra's suggestion. The area where I want to display the widget is now showing along with the the title I set from admin at the time of adding it to a zone. But the content (form elements) I created in the view is not displaying.
The View: (Views/NewsLetterSignupPart/NewsletterSignup.cshtml)
#model Emfluence.Intrust.Models.NewsLetterSignupPart
#{
ViewBag.Title = "Newsletter Signup";
}
#using (Html.BeginForm("NewsletterSignup", "NewsLetter", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="row-fluid">
<div class="span6">
<label>Name</label>
<input type="text" name="txtNewsletterUserName" required maxlength="50" style="width: 95%" />
<label>Email</label>
<input name="txtNewsletterUserEmail" type="email" required maxlength="85" style="width: 95%" />
<button class="btn pull-right">Submit</button>
</div>
</div>
}
Migration.cs
public int UpdateFrom15()
{
ContentDefinitionManager.AlterTypeDefinition(
"NewsletterWidget", cfg => cfg
.WithPart("NewsLetterSignupPart")
.WithPart("CommonPart")
.WithPart("WidgetPart")
.WithSetting("Stereotype", "Widget")
);
return 16;
}
NewsLetterSignupPart.cs
public class NewsLetterSignupPart : ContentPart<NewsletterSignupRecord>
{
[Required]
public string Name
{
get { return Record.Name; }
set { Record.Name = value; }
}
[Required]
public string Email
{
get { return Record.Email; }
set { Record.Email = value; }
}
}
And NewsletterSignupRecord.cs
public class NewsletterSignupRecord : ContentPartRecord
{
public virtual string Name { get; set; }
public virtual string Email { get; set; }
}
Where I am doing wrong?
The Custom Forms module is great if you don't want or need to code something yourself. In case you do want to handle form submissions yourself without using Custom Forms, this is what you could do:
Create a custom module
Create a migrations class that defines a new widget content type (see the docs for details on how to do this. Note: you don't need to create a custom part. You don't even need to create a migrations file to create a content type - you could do it using a recipe file. The nice thing about a migration though is that it will execute automatically when your module's feature is enabled).
Create a view specific for content items of your widget type (e.g. Widget-Newsletter.cshtml).
Inside of this view, write markup that includes a form element and input elements. Have this form post back to your controller.
Create your controller.
In the /admin interface, click Modules, on the Features` tab search for Custom Forms and click Enable. This will add a new Forms admin link on the left.
Next, create a custom content type (under Content Definition) called Newsletter, and add two fields (of type Text Field) called Name and E-mail.
Finally, click Forms and add a new Custom Form. Give it a title: this will be the default URL to access e.g. "Newsletter Form" will have a URL of /newsletter-form by Orchard defaults. Under Content Type select your newly created content type, Newsletter, from the dropdown. Customize anything else you want on this page, and click Publish Now
If you want to make this a widget, edit the content type and add the Widget Part. Create a layer with the rules you need and you can add the "Newsletter" widget to any zone you need on that layer.

Method to display list of available portlets in a portlet

Generally liferay has ADD option for displaying available portlets.
I want them to appear in a drop-down and that should be in a custom plugin-portlet, so I am searching in API which method is retrieving the available portlets, but I didn't find any.
Please help me in this as I am stuck with this and also on selecting from the drop-down the portlet should be added to the page.
The "Add...More" dialog is displayed by the dockbar portlet. You can find the implementation of the UI part of this in Liferay's source in portal-web/docroot/html/portlet/dockbar/add_panel.jsp, which also includes view_category.jsp in the same directory.
While this jsp code is not the prettiest, you'll easily find that PortletLocalService is the one where you find the relevant information, together with an actual sample of how to access the list of portlets by category, sort them according to the current user's locale etc.
As you're asking for more concrete pointers: In add_panel.jsp you can find:
for (PortletCategory curPortletCategory : categories) {
if (curPortletCategory.isHidden()) {
continue;
}
request.setAttribute(WebKeys.PORTLET_CATEGORY, curPortletCategory);
request.setAttribute(WebKeys.PORTLET_CATEGORY_INDEX, String.valueOf(portletCategoryIndex));
%>
<liferay-util:include page="/html/portlet/dockbar/view_category.jsp" />
<%
portletCategoryIndex++;
}
%>
and some excerpts from view_category.jsp:
<%
PortletCategory portletCategory = (PortletCategory)request.getAttribute(WebKeys.PORTLET_CATEGORY);
int portletCategoryIndex = GetterUtil.getInteger((String)request.getAttribute(WebKeys.PORTLET_CATEGORY_INDEX));
// ...
Set<String> portletIds = portletCategory.getPortletIds();
// ...
for (String portletId : portletIds) {
Portlet portlet = PortletLocalServiceUtil.getPortletById(user.getCompanyId(), portletId);
if ((portlet != null) && PortletPermissionUtil.contains(permissionChecker, layout, portlet, ActionKeys.ADD_TO_PAGE)) {
portlets.add(portlet);
// ... and so on
Hope this excerpt helps. See the rest of the file for what you can actually do with the resulting list. Also, Portlet's interface might help if you need more details.

Programmatically Set the Title of a SharePoint Page?

I need to set the page title (Page Title) of a SharePoint page in code. I have already tested
this.Page.Title = "My Page Title";
But this does not change the title when the page loads. Can anyone offer any advise on how to do this?
Thanks, MagicAndi
This blog post by Michael Becker gives a method of modifying the SharePoint Page title using the code below:
ContentPlaceHolder contentPlaceHolder = (ContentPlaceHolder) Page.Master.FindControl("PlaceHolderPageTitle");
contentPlaceHolder.Controls.Clear();
LiteralControl literalControl = new LiteralControl();
literalControl.Text = "My Page Title";
contentPlaceHolder.Controls.Add(literalControl);
If you want to change the page title from a webpart on the page for example, you could use this:
private void ChangeTitle(string newTitle)
{
SPListItem item = SPContext.Current.ListItem;
if (item != null)
{
item[SPBuiltInFieldId.Title] = newTitle;
item.SystemUpdate(false);
}
}
This will only work for a page in the pages library, because the default.aspx page in the root of your site doesn't have an associated listitem. Also don't forget to refresh your page after changing the title.
The SystemUpdate makes sure that 'modified/modified by' information is not updated and that the version number doesn't increase. If you want this information updated, replace it by item.Update();

Resources