Edit a specific User attribute in a page in Concrete5 - attributes

My concrete 5.6.3.1 site has public registration. I have attribute called "I am a ambassador", which is a checkbox that shows in registration and in your profile.
My question is i need this particular attribute to be shown and can be edited inside a specific page.
For Eg: I have a page called Sing up as Ambassador, when going to this page i need to show this particular check box which the user can enable/disable it and save it.(same as profile edit). Anybody there to help me?

NOTICE: This answer is for legacy concrete5 version 6, only some of it will apply for version 7
So for this question, there are a few parts that we'll need to go over.
How do I determine the current logged in user?
$user = new User();
$user_info = UserInfo::getByID($user->getUserID());
How do I get an attribute value?
$user_info->getAttribute('my_attribute_handle');
How do I set an attribute value?
$user_info->setAttribute('my_attribute_handle', $value);
So then in my singlepage view method, I'll have something like:
public function view()
{
$user = new User();
$some_attribute = false;
if ($user->isLoggedIn()) {
$user_info = UserInfo::getByID($user->getUserID());
$some_attribute = $user_info->getAttribute('some_attribute');
}
$this->set('some_attribute', $some_attribute);
}
And then in my singlepage view file, I have:
<input type='checkbox' value='1' name='some_attribute' <?= $some_attribute ? 'checked' : '' ?> />
Where ever my form ends up submitting, I'll do this:
$user = new User();
if ($user->isLoggedIn()) {
$user_info = UserInfo::getByID($user->getUserID());
$user_info->setAttribute('some_attribute', $_REQUEST['some_attribute'] == 1);
}

Related

update customer attribute in frontend account profile form

I'm learning Shopware and I got to something I can't figure out how to solve.
I'm writing a test plugin that adds an attribute to the customer. I've added the correspondent field to the Registration form and it saves its value to the db automatically, like I read somewhere in the docs.
Now I wanted to let the attribute be editable in the account profile page, after the password field. I managed to put the input there, and even show the value from the db. But when I change the value and save, the value its not updated. I don't know if it is just a matter of getting the field name right, or do I need to override something else. Or is it just not possible? Any help on how to achieve this would be greatly appreciated.
Relevant code below:
plugin bootstrap
public function install(InstallContext $context)
{
$service = $this->container->get('shopware_attribute.crud_service');
$service->update('s_user_attributes', 'test_field', 'string');
$metaDataCache = Shopware()->Models()->getConfiguration()->getMetadataCacheImpl();
$metaDataCache->deleteAll();
Shopware()->Models()->generateAttributeModels(['s_user_attributes']);
return true;
}
register/personal_fieldset.tpl
{extends file="parent:frontend/register/personal_fieldset.tpl"}
{block name='frontend_register_personal_fieldset_password_description'}
{$smarty.block.parent}
<div class="register--test-field">
<input autocomplete="section-personal test-field"
name="register[personal][attribute][testField]"
type="text"
placeholder="Test Field"
id="testfield"
value="{$form_data.attribute.testField|escape}"
class="register--field{if $errorFlags.testField} has--error{/if}"
/>
</div>
{/block}
account/profile.tpl
{extends file="parent:frontend/account/profile.tpl"}
{block name='frontend_account_profile_profile_required_info'}
<div class="profile--test-field">
<input autocomplete="section-personal test-field"
name="profile[attribute][testfield]"
type="text"
placeholder="Test Field"
id="testfield"
value="{$sUserData.additional.user.test_field|escape}"
class="profile--field{if $errorFlags.testField} has--error{/if}"
/>
</div>
{$smarty.block.parent}
{/block}
The form type that it's used on registration isn't the same you have on profile.
If you check \Shopware\Bundle\AccountBundle\Form\Account\PersonalFormType::buildForm, you can see
$builder->add('attribute', AttributeFormType::class, [
'data_class' => CustomerAttribute::class
]);
That means the attributes are included on form and they will be persisted. That's why you can save the value on registration form.
On profile you have \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType. And here the attribute isn't added to form builder.
How to extend the ProfileUpdateFormType?
Subscribe Shopware_Form_Builder on Bootstrap (or on a specific Subscriber class)
$this->subscribeEvent('Shopware_Form_Builder', 'onFormBuild');
Create the method onFormBuild to add your logic
public function onFormBuild(\Enlight_Event_EventArgs $event) {
if ($event->getReference() !== \Shopware\Bundle\AccountBundle\Form\Account\ProfileUpdateFormType::class) {
return;
}
$builder = $event->getBuilder();
$builder->add('attribute', AttributeFormType::class, [
'data_class' => CustomerAttribute::class
]);
}
With this approach all attributes are available on your profile form.
Other possibility you have is using the 'additional' property instead of 'attribute' and then subscribe a controller event or hook a controller action to handle your custom data.

Orchard CMS - Extending Users with Fields - exposing values in Blog Post

I'd like to extend the users content definition to include a short bio and picture that can be viewed on every blog post of an existing blog. I'm unsure of what the best method to do this is.
I have tried extending the User content type with those fields, but I can't seem to see them in the Model using the shape tracing tool on the front end.
Is there a way to pass through fields on the User shape in a blog post? If so, what is the best way to do it?
I also have done this a lot, and always include some custom functionality to achieve this.
There is a way to do this OOTB, but it's not the best IMO. You always have the 'Owner' property on the CommonPart of any content item, so in your blogpost view you can do this:
#{
var owner = Model.ContentItem.CommonPart.Owner;
}
<!-- This automatically builds anything that is attached to the user, except for what's in the UserPart (email, username, ..) -->
<h4>#owner.UserName</h4>
#Display(BuildDisplay((IUser) owner))
<!-- Or, with specific properties: -->
<h1>#T("Author:")</h1>
<h4>#owner.UserName</h4>
<label>#T("Biography")</label>
<p>
#Html.Raw(owner.BodyPart.Text)
</p>
<!-- <owner content item>.<Part with the image field>.<Name of the image field>.FirstMediaUrl (assuming you use MediaLibraryPickerField) -->
<img src="#owner.User.Image.FirstMediaUrl" />
What I often do though is creating a custom driver for this, so you can make use of placement.info and follow the orchard's best practices:
CommonPartDriver:
public class CommonPartDriver : ContentPartDriver<CommonPart> {
protected override DriverResult Display(CommonPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Common_Owner", () => {
if (part.Owner == null)
return null;
var ownerShape = _contentManager.BuildDisplay(part.Owner);
return shapeHelper.Parts_Common_Owner(Owner: part.Owner, OwnerShape: ownerShape);
});
}
}
Views/Parts.Common.Owner.cshtml:
<h1>#T("Author")</h1>
<h3>#Model.Owner.UserName</h3>
#Display(Model.OwnerShape)
Placement.info:
<Placement>
<!-- Place in aside second zone -->
<Place Parts_Common_Owner="/AsideSecond:before" />
</Placement>
IMHO the best way to have a simple extension on an Orchard user, is to create a ContentPart, e.g. "UserExtensions", and attach it to the Orchard user.
This UserExtensions part can then hold your fields, etc.
This way, your extensions are clearly separated from the core user.
To access this part and its fields in the front-end, just add an alternate for the particular view you want to override.
Is there a way to pass through fields on the User shape in a blog post?
Do you want to display a nice picture / vita / whatever of the blog posts author? If so:
This could be your Content-BlogPost.Detail.cshtml - Alternate
#using Orchard.Blogs.Models
#using Orchard.MediaLibrary.Fields
#using Orchard.Users.Models
#using Orchard.Utility.Extensions
#{
// Standard Orchard stuff here...
if ( Model.Title != null )
{
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ( (string)Model.ContentItem.ContentType ).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
// And here we go:
// Get the blogPost
var blogPostPart = (BlogPostPart)Model.ContentItem.BlogPostPart;
// Either access the creator directly
var blogPostAuthor = blogPostPart.Creator;
// Or go this way
var blogPostAuthorAsUserPart = ( (dynamic)blogPostPart.ContentItem ).UserPart as UserPart;
// Access your UserExtensions part
var userExtensions = ( (dynamic)blogPostAuthor.ContentItem ).UserExtensions;
// profit
var profilePicture = (MediaLibraryPickerField)userExtensions.ProfilePicture;
}
#tag.StartElement
<header>
#Display(Model.Header)
#if ( Model.Meta != null )
{
<div class="metadata">
#Display(Model.Meta)
</div>
}
<div class="author">
<img src="#profilePicture.FirstMediaUrl"/>
</div>
</header>
#Display(Model.Content)
#if ( Model.Footer != null )
{
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
Hope this helps, here's the proof:

Liferay instanceable portlet does not work properly

I created a custom instanceable portlet, setting the instanceable property to true:
<portlet>
<portlet-name>portletFiltriPTF</portlet-name>
<instanceable>true</instanceable>
<header-portlet-javascript>/js/mediolanumadvice/portletFiltriPTF.js</header-portlet-javascript>
</portlet>
The problem is that I am able to insert the portlet multiple times inside the same page, but only in one of that the content is visible, as you can see in the following image:
Is there anything that I have to do in addition to set that property?
Thank you all,
Marco
Two things to check:
You might have a portlet on page that's (still) non-instanceable because you've added it before you've made your portlet instanceable. They now have different IDs and need to be removed from the page
You might use IDs that are conflicting - e.g. if both portlets create content with the same ID, they'll end up in the DOM. Use these for formatting or any treatment through JS and weird things happen.
I'm not sure if this will help the original poster, but in case anyone else comes across this...
My project is using React and we mount our React root to an element with a static id, referred to as the html root, in index.jsp.
When you try to instantiate multiple instances of the same portlet on one page, subsequent instances will not render because there are multiple elements with the same id.
The solution for this is to create an instance id of your own and create a new element to replace the html root, so that the html root becomes something of a placeholder. Since the html element is removed every time you add a portlet instance, you can reliably mount your React root multiple times without issue.
Here you can see a util I wrote to create a unique root and replace the placeholder (I call it RootUtil):
export default {
makeId: function(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < length; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
},
renderRoot: function(placeholderId) {
// create unique id
var instanceId = this.makeId(10);
var id = "react-root-instance-" + instanceId;
// create unique root
var root = document.createElement("div");
root.setAttribute("id", id);
// find placeholder
const placeholder = document.getElementById(placeholderId);
// replace placeholder
//placeholder.replaceWith(root); **breaks in IE**
placeholder.parentElement.replaceChild(root, placeholder); //workaround
// return the new element
return document.getElementById(id);
}}
And I call it here:
ReactDOM.render(
<Provider store={Store}>
<div>
...
</div>
</Provider>,
RootUtil.renderRoot("html-root"));

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.

Liferay - Link in Dockbar depedent on user group using hook

I am fairly new to the new liferay platform and using hooks. I am adding the currently logged-in user's email next to their name in the Dockbar portlet. I would like this email to link to, when clicked, to a different link depending on whether the user is in either of two groups.
This is what I have written so far in the hook...
//if user is in "group1" show this link
<aui:a cssClass='<%= "user-email" %>' href="link1" title="Gmail">
<%= "(" + HtmlUtil.escape(user.getDisplayEmailAddress() + ")") %>
</aui:a>
//if user is in "group2" show this link
<aui:a cssClass='<%= "user-email" %>' href="link2" title="Outlook">
<%= "(" + HtmlUtil.escape(user.getDisplayEmailAddress() + ")") %>
</aui:a>
How can I achieve this should I be using a <c:if> tag? or can someone exemplify?
You can retrieve the scopeGroupId available in the dockbar's jsp and from that you can retrieve the Group instance.
And then check on which group's page the User is currently and change the href accordingly.
or you can also use the following code in your JSP:
Group group = null;
if(themeDisplay.getLayout().getGroup().isSite()) { // this will tell if the Group is a site or not
group = themeDisplay.getLayout().getGroup(); // fetching the site group instance
}
String href;
if (themeDisplay.getLayout().getGroup().getName().equalsIgnoreCase("group1")) {
href = "group1_link";
} else {
href = "group2_link";
}
Note: themeDisplay is available in Liferay's JSP and to use in your custom portlets you can use <liferay-theme:defineObjects /> tag.
Hope I have understood your question properly and answered accordingly.

Resources