4

J'ai besoin d'une fonctionnalité simple pour ajouter des pages/modifier le contenu de ces pages. J'ai regardé n2, et d'autres outils de pré-construction CMS, mais ceux-ci sont bien avancés pour une simple fonctionnalité de CMS dont j'ai besoin.build MVC CMS

Quelle est la meilleure approche? J'ai déjà une application MVC que je voudrais ajouter/construire une des caractéristiques simples comme:

  1. spécifier un modèle
  2. ajouter des zones à ce modèle
  3. ajouter du contenu via wysiwyg.

Vous ne savez pas par où commencer.

toute information grandement appréciée. grâce

ceci est pour .NET MVC

+0

Est-ce ASP.NET MVC? – drew

+0

Ou, mieux, "Quelle plate-forme utilisez-vous?" - nous pourrions supposer .NET mais nous ne pouvons pas vraiment répondre 'jusqu'à ce que vous nous le fassiez savoir – Murph

+1

Depuis qu'il a regardé N2, je pense qu'il est assez clair qu'il utilise ASP.NET . Et dans cet esprit, beaucoup (débutants en particulier) semblent considérer le terme "MVC" comme synonyme du framework ASP.NET MVC. Mais je devine juste. –

Répondre

10

En supposant que vous utilisez ASP.NET MVC et que vous voulez garder les choses simples, que diriez-vous quelque chose comme ceci:

public abstract class TemplateBase 
{ 
    public abstract string TemplateName { get; } 
} 

public class SingleColumnTemplate : TemplateBase 
{ 
    public override string TemplateName { get { return "Single-column page"; } } 
    public AreaContainer CenterColumn { get; protected set; } 

    public SingleColumnTemplate() 
    { 
     CenterColumn = new AreaContainer("Center column"); 
    } 
} 

public class TwoColumnTemplate : TemplateBase 
{ 
    public override string TemplateName { get { return "Two-column page"; } } 
    public AreaContainer LeftColumn { get; protected set; } 
    public AreaContainer RightColumn { get; protected set; } 

    public TwoColumnTemplate() 
    { 
     LeftColumn = new AreaContainer("Left column"); 
     RightColumn = new AreaContainer("Right column"); 
    } 
} 

// TODO Add more template types 

public class AreaContainer 
{ 
    public string ContainerName { get; set; } 
    public IList<AreaBase> Areas { get; protected set; } 

    public AreaContainer(string name) 
    { 
     ContainerName = name; 
     Areas = new List<AreaBase>(); 
    } 
} 

public abstract class AreaBase 
{ 
    public abstract string AreaName { get; } 
} 

public class HtmlArea : AreaBase 
{ 
    public override string AreaName { get { return "HTML content"; } } 
    public string HtmlContent { get; set; } 
} 

// TODO Add more area types 

public class Page 
{ 
    public int Id { get; set; } 
    public string Title { get; set; } 
    public TemplateBase Template { get; set; } 
} 

L'action du contrôleur pour la modification d'une page existante pourrait ressembler à:

public class PageAdminController : Controller 
{ 
    [HttpGet] 
    ActionResult Edit(int id) 
    { 
     var page = GetPageFromStorageById(id); 
     // TODO If the page is not found, issue 404 
     return View(page); 
    } 

    // ... 
} 

Dans la vue (Views/PageAdmin/Edit.aspx), qui devrait être fortement typé à ViewPage<Page>, vous pouvez utiliser la méthode HtmlHelper.EditorFor(...) pour rendre la vue modèle approprié, à condition que vous avez créé une vue partielle pour chaque type de modèle:

<!-- Inside the edit view for Page (Edit.aspx) --> 
<%: Html.HiddenFor(m => m.Id) %> 
<%: Html.EditorFor(m => m.Title) %> 
<%: Html.EditorFor(m => m.Template) %> 

Dans le dossier Views/PageAdmin/EditorTemplates vous placez des vues d'édition partielle pour chaque modèle et la zone type (ie SingleColumnTemplate.ascx, TwoColumnTemplate.ascx et HtmlArea.ascx). Vous voudrez probablement également créer une vue partielle pour AreaContainer. En ce qui concerne l'action du contrôleur qui reçoit la page éditée, les choses deviennent un peu plus compliquées. Puisque Page possède une propriété du type TemplateBase, qui est une classe abstraite, le DefaultModelBinder ne saura pas comment la peupler. Vous pouvez contourner ce problème en écrivant un classeur de modèle personnalisé qui «connaît» en quelque sorte la classe d'implémentation à instancier. Et comment le saurait-il? Une option que je peux penser est d'inclure un champ caché dans la vue qui contient le nom du type d'exécution réel du modèle de page. C'est un peu un hack, je suppose, mais puisque vous êtes après la simplicité, je pense que tout irait bien. Dans ce cas, inclure juste une propriété appelée, par exemple, RuntimeTypeName dans la TemplateBase classe:

public string RuntimeTypeName { get { return GetType().FullName; } } 

Comme il appelle simplement GetType(), qui est une méthode virtuelle supplantée par tous les types par défaut, il retournera le nom de le type de modèle d'exécution.

Ensuite, vous devez vous assurer que les vues partielles que vous avez créées pour vos implémentations TemplateBase incluent un champ (masqué) pour la propriété TemplateBase.RuntimeTypeName.En d'autres termes, dans SingleColumnTemplate.ascx et TwoColumnTemplate.ascx vous auriez cette ligne:

<%: Html.HiddenFor(m => m.RuntimeTypeName) %> 

Un liant modèle qui utilise ces informations pour créer le type de modèle pourrait ressembler à ceci:

/// <summary> 
/// Model binder hack that builds upon the DefaultModelBinder, 
/// but that can detect the "proper" subclass/implementing class 
/// type for a model, assuming the name of that type is contained 
/// in a field called "RuntimeTypeName". 
/// </summary> 
public class InheritanceSupportingModelBinder : DefaultModelBinder 
{ 
    // Assume that the name of the field that contains the 
    // runtime type name is called "RuntimeTypeName" 
    public const string RuntimeTypeNameField = "RuntimeTypeName"; 
    private Type RuntimeType { get; set; } 

    // This method is called by the DefaultModelBinder to find out which 
    // properties of the current model that it should attempt to bind 
    protected override PropertyDescriptorCollection GetModelProperties(
     ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     // If we have found out the runtime type of the model through 
     // looking at the "special" field above, use the properties of that type. 
     // Otherwise, use the default behavior. 
     if (RuntimeType != null) 
     { 
      return TypeDescriptor.GetProperties(RuntimeType); 
     } 
     else 
     { 
      return base.GetModelProperties(controllerContext, bindingContext); 
     } 
    } 

    // This method is called by the DefaultModelBinder when it 
    // tries to create an instance of the model class. If the 
    // class is abstract, an exception will be thrown. Therefore 
    // we try to read the name of the actual type from the 
    // RuntimeTypeName (hidden) field and return an instance of that type. 
    protected override object CreateModel(ControllerContext controllerContext, 
              ModelBindingContext bindingContext, 
              Type modelType) 
    { 
     if (bindingContext.ValueProvider.ContainsPrefix(
      bindingContext.ModelName + "." + RuntimeTypeNameField)) 
     { 
      var result = bindingContext.ValueProvider.GetValue(
       bindingContext.ModelName + "." + RuntimeTypeNameField); 

      if (result != null && !string.IsNullOrEmpty(result.AttemptedValue)) 
      { 
       // Check that the type indicated by the hidden field is really 
       // a subclass of (or implementing) the indicated base class 
       var tempType = Type.GetType(result.AttemptedValue); 
       if (modelType.IsAssignableFrom(tempType)) 
       { 
        RuntimeType = modelType = tempType; 
       } 
      } 
     } 
     return base.CreateModel(controllerContext, bindingContext, modelType); 
    } 
} 

Avertissement: Je suis un débutant à ASP.NET MVC moi-même, donc ce classeur modèle pourrait bien être défectueux. Je l'ai mis ensemble en regardant un peu le code source pour DefaultModelBinder et par essais et erreurs. C'est juste un exemple, mais d'après mes tests (rapides et sales), ça semble fonctionner.

Bien sûr, vous devez vous inscrire dans le Global.asax pour qu'il botter:

ModelBinders.Binders.Add(
    typeof(TemplateBase), 
    new InheritanceSupportingModelBinder()); 

Mais nous ne sommes pas fait! Rappelez-vous que la collection AreaContainer.Areas est de type IList<AreaBase> - et puisque AreaBase est aussi une classe abstraite, nous devons appliquer le même hack pour qu'il soit lié correctement. Autrement dit, ajoutez la propriété RuntimeTypeName à la classe AreaBase et enregistrez notre classeur modèle personnalisé pour la classe AreaBase dans Global.asax.

Pourvu que nous avons suivi toutes ces étapes jusqu'à présent, nous pourrions avoir une méthode d'action sur notre PageAdminController pour gérer les modifications des pages qui ressemble à ceci:

[HttpPost] 
public ActionResult Edit(Page page) 
{ 
    if (!ModelState.IsValid) 
    { 
     return View(page); 
    } 
    // TODO Save page to database or whatever 
    // TODO Redirect to page index 
} 

Les méthodes d'action pour la création d'une nouvelle page À gauche en tant qu'exercice, cela ne devrait pas être si difficile (l'utilisateur sélectionne un modèle dans une liste, le formulaire approprié est affiché, une action de post-traitement comme ci-dessus).

L'affichage des pages doit être trivial, il suffit d'utiliser le HtmlHelper.DisplayFor(...) au lieu de EditorFor(...), créer les vues partielles correspondantes et vous avez défini.

Pour l'édition WYSIWYG de contenu, vous souhaiterez probablement utiliser un composant tiers. CKEditor, TinyMCE, YUI Rich Text Editor et Telerik Editor sont quelques exemples.

C'était mon point de vue là-dessus! Tous les commentaires sont les bienvenus Comme je l'ai mentionné, j'apprends ASP.NET MVC moi-même et ce serait génial si mes erreurs étaient signalées par des gens qui connaissent mieux.

+0

Génial. Cela ressemble à quelque chose que je vais utiliser. merci – ShaneKm

+1

@Shane Dommage que vous preniez un travail important de gens comme ça, et ne peut même pas être dérangé de cliquer sur un vote/accepter de temps en temps en retour. –

+0

en ce qui concerne la création de nouvelles pages. Serait-ce de nouvelles pages générées OU auriez-vous une page mise à jour dynamiquement avec le contenu de db? – ShaneKm