Orchard CMS, Merging templates - orchardcms

I am developing a Widget to show Content pushes on the home page. The push model is as below.
public class PushRecord : ContentPartRecord
{
public virtual string Header { get; set; }
public virtual string Text { get; set; }
public virtual string Url { get; set; }
}
On the admin, I modified the ContentType of the Push Widget to add Media Picker Field. I would like to make a hyper link around the image with Url provided by PushPart. Npw the widget is rendered by two templates, Parts.Push.cshtml and Fields.MediaPicker-PushWidget-Image.cshtml. How do I merge these two and make my Push rendering possible? Any help is greatly appreciated.

Maybe try suppressing display of mediapickerfield via placement.info, and then explicitly render the image with the hyperlink from the .cshtml of the PushPart. You can access the MediaPickerField url like this:
#{
var pushPart = Model.ContentPart;
var photoUrl = pushPart.MediaPickerFieldName.Url;
}
<img src="#photoUrl" ... />

Related

Add a new menu section in admin page in Orchard CMS

I use Orchard CMS 1.10.1. I have created a content type, now I want to have a section menu in admin page to have specific links for this content type. I want it include New link and content item list for this content type.
How can i achieve this? preferably with no editing in the code.
You can't do this without code, at the same time you will need a small code chunk to achieve this, like the following code:
public class AdminMenu : INavigationProvider {
public Localizer T { get; set; }
public string MenuName {
get { return "admin"; }
}
public void GetNavigation(NavigationBuilder builder) {
builder
.Add(T("Your Content Type Display Name"), "1", menu => menu
.Action("List", "Admin", new { area = "Contents", id = "YourContentTypeName" }));
}
}

display selected file after view reload in ASP.NET MVC 5

I am working on a project in ASP.NET MVC 5, there is a requirement to upload image file, I can upload the image to the server, what I am facing is when there is an error in the view submitted I return the view model with some error message to be displayed on the view so that the user is not requested to select the image again, all the form fields data in the view are getting populated except the file which previously selected is not. Please help me the way to achieve this.
Here is the code I have to select the image
#Html.TextBoxFor(model => Model.Logo, null, new { type = "file"})
here is the model field
[DataType(DataType.Upload)]
public HttpPostedFileBase Logo
{
get;
set;
}
For security reasons, you cannot set the value of a file input in the controller, so if you want to avoid having the user select the file again if you return the view, you must first save the file (it could be to a temporary location) and display an indicator to the user that the file has been uploaded. For example, your model might contain the following properties
public HttpPostedFileBase Logo { get; set; }
public string LogoFileName { get; set; }
public string LogoPath { get; set; }
and in the controller method, save the file and set the FileName (i.e. model.LogoFileName = Logo.FileName;) and Path properties. Then in the view, add a conditional statement to render the FileName if it exists, or display the file input if it does not, for example
if(Model.LogoFileName == null)
{
#Html.TextBoxFor(m => m.Logo, new { type = "file" })
}
else
{
#DisplayFor(m => m.LogoFileName)
}

Adding the target parameter to the Custom Link MenuItem in Orchard CMS

I have created my navigation menus in Orchard using a mix of Content Item and Custom Link Elements (parts of the website are outside the scope of the CMS). Now there are a couple of links that I need to open in a new window/tab, basically the target="_blank" behaviour.
SInce the original Custom Link does not have any parameters I tried to create an extended version of it. In the admin backend I went to "Content definition" looked up Custom Link and tried to create a copy of it, then add a target field that I could check for and use in my theme's Menu.cshtml file.
However I can't even get the basic carbon copy of the Custom Link item working. It has the same stereotype, same Parts, same Forms (none) as the original Custom Link, and it does appear in the list of items on the admin -> navigation window. However the item does not have a field for the URL/link. It only has the field for Menu Text, nothing else.
So my question is 2-tiered:
How can I get a carbon copy of the Custom Link item type working in my Orchard backend navigation?
When I have my copy of the Custom Link working and add a text field named target, how can I access its value in the Menu.cshtml view?
(I tried simply adding a URL field to my copy, that would then show up in the navigation editor, however the navigation itself would ignore it in the output and create a link to the content item id instead).
Any help is greatly appreciated!
Edit: Here are some screenshots to better illustrate the problem, maybe they can help pin down the problem.
Seems like you've done everything right. Please double check if MenuItemPart is there. This part is responsible for holding the URL information and displaying an editor for it. Not sure if this part is attachable though - if it's not, then make it so in the Content Definition\Parts pane.
Instead of hardwiring things inside Menu.cshtml, you should create a file named MenuItemLink-[YourTypeName].cshtml. This shape file will be used to display your custom menu items. Then you can access any fields via Model.Content object, eg. Model.Content.YourTypeName.FieldWithTargetName.Value.
You need to use the MenuItemPart because it has a few important functions integrated into Orchard.Core.
This works fine:
AdvancedMenuItemPartRecord:
public class AdvancedMenuItemPartRecord : ContentPartRecord
{
public virtual string Target { get; set; }
public virtual string Classes { get; set; }
}
AdvancedMenuItemPart:
public class AdvancedMenuItemPart : ContentPart<AdvancedMenuItemPartRecord>
{
public string Target
{
get { return Retrieve(x => x.Target); }
set { Store(x => x.Target, value); }
}
public string Classes
{
get { return Retrieve(x => x.Classes); }
set { Store(x => x.Classes, value); }
}
}
AdvancedMenuItemPartDriver:
public class AdvancedMenuItemPartDriver : ContentPartDriver<AdvancedMenuItemPart>
{
protected override string Prefix
{
get { return "AdvancedMenuItem"; }
}
protected override DriverResult Editor(AdvancedMenuItemPart part, dynamic shapeHelper)
{
return ContentShape("Parts_AdvancedMenuItem_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/AdvancedMenuItem", Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(AdvancedMenuItemPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
AdvancedMenuItemPartHandler (ActivatingFilter add MenuItemPart to your AdvancedMenuItem dynamicaly):
public class AdvancedMenuItemPartHandler : ContentHandler
{
public AdvancedMenuItemPartHandler(IRepository<AdvancedMenuItemPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
Filters.Add(new ActivatingFilter<MenuItemPart>("AdvancedMenuItem"));
}
}
Placement.info:
<Place Parts_AdvancedMenuItem_Edit="Content:11"/>
Migrations:
public int UpdateFrom2()
{
SchemaBuilder.CreateTable("AdvancedMenuItemPartRecord",
table => table
.ContentPartRecord()
.Column<string>("Target")
.Column<string>("Classes")
);
ContentDefinitionManager.AlterPartDefinition("AdvancedMenuItemPart", part => part
.WithDescription(""));
ContentDefinitionManager.AlterTypeDefinition("AdvancedMenuItem", cfg => cfg
.WithPart("AdvancedMenuItemPart")
.WithPart("MenuPart")
.WithPart("CommonPart")
.WithIdentity()
.DisplayedAs("Custom Link Advanced")
.WithSetting("Description", "Custom Link with target and classes fields")
.WithSetting("Stereotype", "MenuItem")
// We don't want our menu items to be draftable
.Draftable(false)
// We don't want the user to be able to create new ActionLink items outside of the context of a menu
.Creatable(false)
);
return 3;
}
MenuItemLink-AdvancedMenuItem.cshtml:
#{
var advancedPart = Model.Content.AdvancedMenuItemPart;
var tag = new TagBuilder("a");
tag.InnerHtml = WebUtility.HtmlDecode(Model.Text.Text);
tag.MergeAttribute("href", Model.Href);
if (!string.IsNullOrWhiteSpace(advancedPart.Target)) {
tag.MergeAttribute("target", advancedPart.Target);
}
if (!string.IsNullOrWhiteSpace(advancedPart.Classes))
{
tag.AddCssClass(advancedPart.Classes);
}
}
#Html.Raw(tag.ToString(TagRenderMode.Normal))

Can't seem to expand an Image Navigation property with Breeze JS

I am using MVC, Entity Framework, Durandal and Breeze JS. I've got a user which looks like such (simplified):
public class User : EntityBase<Guid>, IAggregateRoot
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("UserImage")]
public virtual Guid? ImageId { get; set; }
public virtual UserImage UserImage { get; set; }
}
The UserImage class looks like such. I know I should limit the size of the Image. (Maybe this is the issue?):
public class UserImage
{
public Guid Id { get; set; }
[MaxLength]
public byte[] Image { get; set; }
}
I've got an api function on the server to get the current user:
public IQueryable<User> GetCurrentUser()
{
IPrincipal principal = HttpContext.Current.User;
var users = _uow.Users.FindBy(u => u.UserName.Equals(principal.Identity.Name));
if (!users.Any())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized));
}
return users;
}
And two calls on the client which get the current user. The first is in the shell:
function loadCurrentUser() {
return uow.CurrentUser.all().then(function (newUser) {
log('Welcome to the Site ' + newUser[0].FullName() + '!', newUser[0], true);
config.CurrentUser(newUser[0]);
return true;
});
}
The second is in a ManageUser viewmodel:
function activate() {
return uow.CurrentUser.all(['UserImage']).then(function (user) {
self.CurrentUser(user[0]);
return $.when(init()).then(boot());
}).fail(function() {
return router.activate('accounts/login');
});
}
Now I can load an Image into the ManageUser page and save and in fiddler it shows that the ImageId and Image are being sent across to the server. Then I checked the BeforeSaveEntity intercept and shows two entities being saved.
Updated User with ImageId set
New UserImage
The data is also visible in the database. Now when I refresh the Manage User page I can see the two GetCurrentUser calls in fiddler.
From the shell call I can see that the User is being returned and an ImageId is set but no UserImage was sent over because didn't expand the query.
From the Manage User call I see the User is returned but only the ImageId is sent over and the Image object was OMITTED from the JSON.
Has anyone come across this issue with images? All my other expands appear to be working correctly. Does anyone have any examples on using breeze to save just the filepath to the image and possibly using windows azure for media storage?
I know this probably won't answer your question but I would propose not sending the byte array to the client and rather have an Image Handler on the server side that takes an ImageId as a parameter and then return the image with the relevant Content Type set. An example of this can be found here.
By using this approach you could reference your image from HTML using an img tag with the source set to the Image Hander with the relevant ImageId.
An example using knockout for data binding would be:
<a data-bind="attr: {href: '/Image/' + User.ImageId()}"></a>
This approach enables you to easily add caching on both the server and client which will improve performance. It also removed the need to convert the byte array to an image on the client side, which may or may not be a pain.
Edit:
When saving the managed user, post the Image to an Upload action on the ImageHandler (have a look at this article). This action must return the new Id of the image. After you've received the new Id, update the User.ImageId on client side and call SaveChanges on breeze.

ASP.NET MVC 4 Remember page index

In a ASP.NET MVC 4 app I have a view with a paged list (just a simple table, no telerik grid or anything like that). New values are fetched from the database when the user pages through the list.
On every row in that table there is an edit button, when clicking the button you are presented with an edit view and when you click save in that view you are redirected back to the view with the paged list.
The urls for the list view looks like this
http://localhost/Items/Page/1
http://localhost/Items/Page/2
The route looks like this
routes.MapRoute(
name: "ItemsList",
url :"Items/Page/{page}",
defaults: new { controller = "Items", action = "Index", page = 1 },
constraints: new {page = #"\d+"}
);
My question is this: what is the preferred, most common way to store away the referring url, so when done editing an item, I can redirect the user back to the correct url
http://localhost/Items/Page/2
and not just to
http://localhost/Items
I've tried splitting up
Request.UrlReferrer.PathAndQuery
and storing those values around, and then build the url from those values but I have a feeling there is a much better solution to this problem. Any suggestions?
Update
Right now I'm thinking that I could put the UrlReferrer.PathAndQuery (if there are any values) as a property on the view model for the edit screen and then use that when deciding on where to redirect after a save.
Any thoughts out there on that approach?
Here is my final solution to the problem, it's not super elegant but it works.
I added a property to the View model that could store the url. That value get's stored in a hidden field.
public class SkillEditModel
{
public int Id { get; set; }
public string Name { get; set; }
public string RedirectBackToUrl { get; set; }
}
In the controller Edit(GET) method I store the value with the view model
if (!Request.UrlReferrer == null)
{
model.RedirectBackToUrl = Request.UrlReferrer.PathAndQuery;
}
And finally after saving the changes in Edit (POST) I did this
if (!string.IsNullOrWhiteSpace(model.RedirectBackToUrl))
{
return new RedirectResult(model.RedirectBackToUrl);
}
return RedirectToAction("Index");

Resources