Dataaccess problem

Topics: Writing modules
Aug 11, 2011 at 2:33 PM
Edited Aug 11, 2011 at 2:37 PM

 

Hey, ok so far i managed to make My Teaser Module do the following "Add a Teaser to Database" , and Edit/Delete Teasers in the Database.

I Also have a Widget which has 5 custom Fields ( TeaserOne to TeaserFive ). What i want to do is enter the Name of a Teaser in one of the fields, and then want the widget to compare the Name in TeaserOne with the TeaserNames in the other Database table and if it matches, show that particular Teaser on the page.

However i cant get that part to work, it won't show anything.. unless i try to render the model into an HtmlString, then it shows and compiler error

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'System.Web.HtmlString.HtmlString(string)' has some invalid arguments

 

@{ var Teaser = new HtmlString(@Model.TeaserText); }

 

So i don't know what exactly it passes on there, as it won't show anything if i just try to view the Model, but surely it passes on something otherwise that error wouldnt occur... i guess.

Well here's the codes.. help appreciated

 

Teaser.cshtml

@{ var Teaser = new HtmlString(@Model.TeaserText); }

<div class="TeaserHeader">@Model.Headline</div>
<div class="TeaserInformation">@Teaser</div>

 

TeaserWidget.cshtml

@model Teaser.Models.TeaserWidgetPart

<fieldset>
    <label class="sub" for="TeaserOne">@T("TeaserOne")</label><br />
    @Html.TextBoxFor(m => m.TeaserOne, new { @class = "text" })<br />
    <label class="sub" for="TeaserTwo">@T("TeaserTwo")</label><br />
    @Html.TextBoxFor(m => m.TeaserTwo, new { @class = "text" })<br />
    <label class="sub" for="TeaserThree">@T("TeaserThree")</label><br />
    @Html.TextBoxFor(m => m.TeaserThree, new { @class = "text" })<br />
    <label class="sub" for="TeaserFour">@T("TeaserFour")</label><br />
    @Html.TextBoxFor(m => m.TeaserFour, new { @class = "text" })<br />
    <label class="sub" for="TeaserFive">@T("TeaserFive")</label><br />
    @Html.TextBoxFor(m => m.TeaserFive, new { @class = "text" })<br />
</fieldset>

 

 

TeaserWidgetDriver.cs

using Teaser.Models;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement;
using System.Linq;

namespace Teaser.Drivers
{
    public class TeaserWidgetDriver : ContentPartDriver<TeaserWidgetPart>
    {
        private readonly IContentManager _contentManager;

        public TeaserWidgetDriver(IContentManager contentManager) {
            _contentManager = contentManager;
        }

        protected override DriverResult Display(TeaserWidgetPart part, string displayType, dynamic shapeHelper)
        {
            var teasers = _contentManager.Query<TeaserPart, TeaserPartRecord>("Teaser")
                .Where(tp => tp.Name == part.TeaserOne)
                .List()
                .Select(tp => new TeaserViewModel
                {
                    Headline = tp.Headline,
                    TeaserText = tp.TeaserText
                }).ToList();

            return ContentShape("Parts_Teaser",
                () => shapeHelper.Parts_Teaser(Teaser: teasers, ContentPart: part));
        }

        //GET
        protected override DriverResult Editor(TeaserWidgetPart part, dynamic shapeHelper)
        {
            return ContentShape("Parts_TeaserWidget_Edit",
                () => shapeHelper.EditorTemplate(
                    TemplateName: "Parts/TeaserWidget",
                    Model: part,
                    Prefix: Prefix));
        }

        //POST
        protected override DriverResult Editor(
            TeaserWidgetPart part, IUpdateModel updater, dynamic shapeHelper)
        {
            updater.TryUpdateModel(part, Prefix, null, null);
            return Editor(part, shapeHelper);
        }
    }
}

 

 

TeaserModel.cs using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace Teaser.Models {
    public class TeaserPartRecord : ContentPartRecord
    {
    public virtual string Name { get; set; }
    public virtual string Headline { get; set; }
    public virtual string TeaserText { get; set; }
    }

    public class TeaserPart : ContentPart<TeaserPartRecord>
    {
    [Required]
    public string Name
    {
        get { return Record.Name; }
        set { Record.Name = value; }
    }

    [Required]
    public string Headline
    {
        get { return Record.Headline; }
        set { Record.Headline = value; }
    }

    [Required]
    public string TeaserText
    {
        get { return Record.TeaserText; }
        set { Record.TeaserText = value; }
    }
  }
} 


TeaserWidgetModel.cs


using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;

namespace Teaser.Models
{
    public class TeaserWidgetPartRecord : ContentPartRecord
    {
        public virtual string TeaserOne { get; set; }
        public virtual string TeaserTwo { get; set; }
        public virtual string TeaserThree { get; set; }
        public virtual string TeaserFour { get; set; }
        public virtual string TeaserFive { get; set; }
    }

    public class TeaserWidgetPart : ContentPart<TeaserWidgetPartRecord>
    {
        [Required]
        public string TeaserOne
        {
            get { return Record.TeaserOne; }
            set { Record.TeaserOne = value; }
        }

        public string TeaserTwo
        {
            get { return Record.TeaserTwo; }
            set { Record.TeaserTwo = value; }
        }

        public string TeaserThree
        {
            get { return Record.TeaserThree; }
            set { Record.TeaserThree = value; }
        }

        public string TeaserFour
        {
            get { return Record.TeaserFour; }
            set { Record.TeaserFour = value; }
        }

        public string TeaserFive
        {
            get { return Record.TeaserFive; }
            set { Record.TeaserFive = value; }
        }
    }
}

Dunno what other files are relevant, but i assume the error is within TeaserWidgetDriver.cs ... can anyone tell me what's going wrong ? :/

incase it matters

Databases are called TeaserPartRecord and TeaserWidgetPartRecord


Coordinator
Aug 11, 2011 at 7:15 PM

Can you try this:

@{ var Teaser = new HtmlString((string)@Model.TeaserText); }
Aug 11, 2011 at 8:03 PM
Edited Aug 11, 2011 at 8:06 PM

Yeah sure i can try that at work tomorrow , but i doubt that's my problem.

Same View Model works fine in admin panel and when i "view" the teaser from admin panel.

So i guess my problem is somewhere in the widget driver... at least it's not showing my teasers or any part of it... i'm not even sure if it reads the data i want it to read... have you looked over that,too ? Does that look somewhat rightish ? ^^

Aug 12, 2011 at 8:16 AM
sebastienros wrote:

Can you try this:

@{ var Teaser = new HtmlString((string)@Model.TeaserText); }

ok i tried this, it solves the exception error, but still doesn't show anything.

any ideas for the driver ?

Aug 12, 2011 at 9:56 AM
Edited Aug 12, 2011 at 10:00 AM

ok i'm just lost with trying.. ._.

i have two tables TeaserPartRecord and TeaserWidgetPartRecord.

TeaserPartRecord looks like this

ID ------ Name ------ Headline ------ TeaserText

XX------Name1------Headline1------TeaserText1...

XY------Name2------Headline2------TeaserText2...

 

TeaserWidgetPartRecord

ID ------ TeaserOne ------ TeaserTwo ------ TeaserThree ------ TeaserFour ------ TeaserFive

XYY------ Name1 ------ NULL ---------------- NULL ------------ NULL ------------ NULL

XYZ ------ Name1 ----- Name 2 -------------- NULL ------------ NULL ------------ NULL

 

for example. How would my Driver have to look like that i Compare the Names in TeaserOne to Five in TeaserWidgetPartRecord with Name in TeaserPartRecord and if a match is there, get Headline and TeaserText from that Record and post it to the view ? ...

 

looks like that right now, but it's obviously not working...

 

        protected override DriverResult Display(TeaserWidgetPart part, string displayType, dynamic shapeHelper)
        {
            var teasers = _contentManager.Query<TeaserPart, TeaserPartRecord>("Teaser")
                .Where(tp => tp.Name == part.TeaserOne)
                .Where(tp => tp.Name == part.TeaserTwo)
                .Where(tp => tp.Name == part.TeaserThree)
                .Where(tp => tp.Name == part.TeaserFour)
                .Where(tp => tp.Name == part.TeaserFive)
                .List()
                .Select(tp => new TeaserViewModel
                {
                    Headline = tp.Headline,
                    TeaserText = tp.TeaserText
                }).ToList();

            return ContentShape("Parts_Teaser",
                () => shapeHelper.Parts_Teaser(Teaser: teasers, ContentPart: part));
        }

EDIT: Or what might make it a little easier...

Change the EditorTemplate


@model Teaser.Models.TeaserWidgetPart

<fieldset>
    <label class="sub" for="TeaserOne">@T("TeaserOne")</label><br />
    @Html.TextBoxFor(m => m.TeaserOne, new { @class = "text" })<br />
    <label class="sub" for="TeaserTwo">@T("TeaserTwo")</label><br />
    @Html.TextBoxFor(m => m.TeaserTwo, new { @class = "text" })<br />
    <label class="sub" for="TeaserThree">@T("TeaserThree")</label><br />
    @Html.TextBoxFor(m => m.TeaserThree, new { @class = "text" })<br />
    <label class="sub" for="TeaserFour">@T("TeaserFour")</label><br />
    @Html.TextBoxFor(m => m.TeaserFour, new { @class = "text" })<br />
    <label class="sub" for="TeaserFive">@T("TeaserFive")</label><br />
    @Html.TextBoxFor(m => m.TeaserFive, new { @class = "text" })<br />
</fieldset>

Instead of TextBoxes where i have to enter the name of  a Teaser i'd rather have a DropDownList which gets filled with all available Teasers, so that in the end i only have to select the one's i want ( or leave it to NULL / NONE for no teaser in that spot ), but i can't figure out how to let one Part with an own Record access Data from another Part...

Coordinator
Aug 12, 2011 at 3:21 PM

Have you defined the shapes inside Placement.info ?

Aug 12, 2011 at 3:42 PM
<Placement>
    <Place Parts_Teaser_Edit="Content:3"/>
    <Place Parts_Teaser="Content:3"/>
    <Place Parts_TeaserWidget_Edit="Content:3"/>
    <Place Parts_TeaserWidget="Content:3"/>
</Placement>

Yeah... somehow.. however the Zone they are supposed to be shown in is not Content. It has a different name, if i use that one however i don't see the parts in AdminMenu anymore...
Aug 15, 2011 at 9:00 AM

i'm confused by the Placement.info

isn't it supposed to be place partname = "ZONE:ORDER" ?

http://img821.imageshack.us/img821/8962/placement.jpg

i got that now, if i view my teaser with placement Content:3 it shows in Content

if i do the same with my Zone SubInformationZone:3 it doesn't show. I already removed the check where it would only display that Zone if it was not null , but still.. nothing to see. And this part isn't even done by my widget yet, regardless if it works or not, i'm just viewing the teaser here... So what am i doing wrong ?

Coordinator
Aug 23, 2011 at 1:33 AM

Can you show how you defined the SubInformationZone local zone?

Aug 23, 2011 at 8:15 AM

 

layout.cshtml

var
displaySub = (Model.SubNavigation != null) || (Model.SubInformationZone != null) ; @if (displaySub) { <div id="layout-Sub" class="zone"> <div id="layout-SubNavigation"> @Display(Model.SubNavigation) </div> <div id="layout-Content"> @Display(Model.Content) </div> <div id="layout-SubInformationZone"> @Display(Model.SubInformationZone) </div> </div> }

and css although i guess it doesnt matter
 

 

    #layout-Sub
    {
        margin: 0 0 25px 0;
        width: 960px;
    }
      #layout-SubNavigation
    {
        float: left;
        width: 200px;
    }
 
    #layout-Content
    {
         float: left;
         width: 510px;
         margin: 0 25px 0 25px;
    }
    
    #layout-SubInformationZone
    {
        float: right; width: 200px;
        padding: 0;
        background-color: #fff;
    }

i also tried to remove the != null checks and/or place a dummy widget in that zone so that its not empty.

Coordinator
Aug 23, 2011 at 8:19 AM

Yes, that is not a local zone. For it to be local, it would need to be defined in content.cshtml or one of its alternates. If you want to send a shape to non-local zones, it's possible but you have to do it in code.

Aug 23, 2011 at 3:11 PM

i added this to content.cshtml ( copied it to my Theme, designertools shows path to my theme too)

@using Orchard.Utility.Extensions;
@{
    if (Model.Title != null) {
        Layout.Title = Model.Title;
    }
   
    var contentTypeClassName = ((string)Model.ContentItem.ContentType).HtmlClassify();
}
<article class="content-item @contentTypeClassName">
    <header>
        @Display(Model.Header)
        @if (Model.Meta != null) {
        <div class="metadata">
            @Display(Model.Meta)
        </div>
        }
    </header>
    @Display(Model.Content)
    @if(Model.SubInformationZone !=null)
    {  
        @Display(Model.SubInformationZone)
    }
    @if(Model.Footer != null) {
    <footer>
        @Display(Model.Footer)
    </footer>
    }
</article>

if i change placement.info to SubInformationZone now, it still appears in Content... what am i missing ?

Coordinator
Aug 23, 2011 at 7:35 PM

Ah yes, I may know what that is. Can you check that your template is being used, by inserting some random text into it?

Aug 23, 2011 at 7:39 PM

oh yeah that works. i can put any other widget there. i also had those teasers there via html widget, but since i have several pages i wanted to store them global and just add whichever i want there. that doesn't work.

so it seems to be my module that doesnt do what i want it to do.

if you want i can pack it tomorrow at work and just up it somewhere to take a look at it, that might be easier.

Coordinator
Aug 23, 2011 at 7:50 PM

And you're sure you got the shape name right in placement? Can you move it around successfully within content for example?

Aug 24, 2011 at 12:33 PM

ok this is getting wierder and wieder..

i added an html widget with position 1 and added it to a layerrule for url("~/Contents/Item/Display/*") so that its shown when i view my Item.

  <Place Parts_Teaser="Content:10"/>

removing this part from placement.info let's my teaser disappear. however, the number doesnt seem to affect it at all. wether it's 1 or 999 its always above of that html widget.

i can also write   <Place Parts_Teaser="Footer"/> or   <Place Parts_Teaser="Header"/> but it will still appear in my Content Zone... any zone that is not mentioned in content.cshtml let's it disappear aswell

i upped it here: http://uploaded.to/file/p0ltqyed

since it's not working i didnt want to put in on gallary.

Aug 24, 2011 at 3:13 PM

Ok, as i just figured out, the whole point of this module, can also be solved with a single Layer per Teaser, which is actually even less effort...

So actually there is no real need for this module anymore, i'm just curious now why it didnt work for any future module devolpment. So if you could ever take a look i'd appreciate it.

Coordinator
Aug 24, 2011 at 7:41 PM

What you are describing seems normal to me: widgets are placed into top-level zones, whereas parts are placed in local zones within the content zone where their content item is being displayed. For parts of the main content item, that is the local content zone from the content template that is displayed in the top-level content zone defined in layout, for parts of a widget it's the content zone from the content.cshtml that was rendered as part of the list of widgets. If you put a widget into the top-level content zone, a list of widgets will be rendered in the content zone from layout. In each list item, the widget (a content item) will render as a content template, which will have a content zone, where any parts placed in content will appear. If the controller puts a content item into the top-level content zone, a content template will get displayed there, which will define a local content zone, which is where any parts placed in content will be displayed. I guess that's a wee bit confusing, yes. I'd like us to enable top-level zones in placement in a future release.

And of course, when you are placing parts in footer or header, that really is the footer and header that are defined in the content template that will get displayed in the top-level content zone. At least that's how I understand it.

Aug 24, 2011 at 8:20 PM

alright, that was confusing.. ^^ but i guess i got the message.

Well for now my problem is solved and i'll hopefully get this figured out with the next module i'll build.. or try to,

thanks for that explanation.