Pertanyaan membangun MVC CMS


Saya memerlukan fungsi sederhana untuk menambahkan halaman / mengubah konten pada halaman-halaman itu. Saya telah melihat n2, dan alat-alat pra-bangun CMS lainnya tetapi ini adalah cara untuk maju untuk fungsi CMS sederhana yang saya butuhkan.

Apa pendekatan terbaiknya? Saya sudah memiliki aplikasi MVC yang ingin saya tambahkan / bangun fitur sederhana seperti:

  1. tentukan template
  2. tambahkan area ke template itu
  3. tambahkan konten melalui wysiwyg.

tidak yakin harus mulai dari mana.

info apa pun sangat dihargai. Terima kasih

ini untuk .NET MVC


4
2017-11-04 13:18


asal


Jawaban:


Dengan asumsi Anda menggunakan ASP.NET MVC dan Anda ingin membuatnya tetap sederhana, bagaimana dengan hal seperti ini:

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; }
}

Tindakan pengontrol untuk mengedit halaman yang sudah ada dapat terlihat seperti ini:

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);
    }

    // ...
}

Dalam tampilan (Views/PageAdmin/Edit.aspx), yang harus diketik dengan kuat ViewPage<Page>, Anda dapat menggunakan HtmlHelper.EditorFor(...) metode untuk membuat tampilan template yang sesuai, asalkan Anda telah membuat tampilan parsial untuk setiap jenis template:

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

Di dalam folder Views/PageAdmin/EditorTemplates Anda kemudian akan menempatkan tampilan edit parsial untuk setiap jenis template dan area (mis. SingleColumnTemplate.ascx, TwoColumnTemplate.ascx dan HtmlArea.ascx). Anda mungkin juga ingin membuat tampilan parsial untuk AreaContainer.

Adapun tindakan pengontrol yang menerima halaman yang diedit, hal-hal menjadi sedikit lebih rumit. Sejak Page memiliki properti tipe TemplateBase, yang merupakan kelas abstrak, yang DefaultModelBinder tidak akan tahu bagaimana mengisinya. Anda bisa mendapatkan ini dengan menulis pengikat model kustom yang entah bagaimana "tahu" yang menerapkan kelas untuk instantiate. Dan bagaimana ia tahu itu? Satu opsi yang dapat saya pikirkan adalah menyertakan bidang tersembunyi di tampilan yang berisi nama jenis runtime aktual dari templat halaman. Ini sedikit hack, saya kira, tapi karena Anda mengejar kesederhanaan saya pikir itu akan baik-baik saja. Dalam hal ini, cukup sertakan properti yang disebut, misalnya, RuntimeTypeName dalam TemplateBase kelas:

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

Karena itu hanya panggilan GetType(), yang merupakan metode virtual yang dikesampingkan oleh semua jenis secara default, ini akan mengembalikan nama tipe template runtime.

Maka Anda harus memastikan bahwa sebagian pandangan yang Anda buat untuk Anda TemplateBase implementasi termasuk bidang (tersembunyi) untuk TemplateBase.RuntimeTypeName milik. Dengan kata lain, di SingleColumnTemplate.ascx dan TwoColumnTemplate.ascx Anda akan memiliki baris ini:

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

Sebuah pengikat model yang menggunakan informasi ini untuk membuat jenis template yang tepat dapat terlihat seperti ini:

/// <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);
    }
}

Penolakan: Saya seorang pemula di ASP.NET MVC sendiri, jadi pengikat model ini mungkin rusak. Saya telah menggabungkannya dengan melihat kode sumber DefaultModelBinder dan dengan trial-and-error. Itu hanya sebuah contoh, tetapi menurut pengujian saya (cepat dan kotor) tampaknya itu berhasil.

Tentu saja Anda harus mendaftarkannya di Global.asax agar dapat menendang:

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

Tapi kita belum selesai! Ingat bahwa AreaContainer.Areas koleksi adalah tipe IList<AreaBase> - dan sejak itu AreaBase juga kelas abstrak, kita harus menerapkan hack yang sama agar terikat dengan benar. Yaitu, tambahkan RuntimeTypeName properti ke AreaBase kelas dan daftar pengikat model kustom kami untuk AreaBasekelas masuk Global.asax.

Asalkan kita telah mengikuti semua langkah ini sejauh ini, kita bisa memiliki metode aksi di kami PageAdminController untuk menangani pengeditan halaman yang terlihat seperti ini:

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

Metode tindakan untuk membuat halaman baru dibiarkan sebagai latihan, seharusnya tidak terlalu sulit (pengguna memilih template dari daftar, formulir yang tepat ditampilkan, tindakan pasca-penanganan seperti di atas).

Menampilkan halaman harus sepele, cukup gunakan HtmlHelper.DisplayFor(...) dari pada EditorFor(...), buat tampilan parsial yang sesuai dan Anda tetapkan.

Untuk pengeditan konten WYSIWYG, Anda mungkin ingin menggunakan komponen pihak ketiga. CKeditor, TinyMCE, YUI Rich Text Editor dan Telerik Editor beberapa contoh.

Itu yang saya ambil ini! Semua komentar diterima; seperti yang saya sebutkan saya sedang belajar ASP.NET MVC sendiri dan akan sangat bagus jika kesalahan saya ditunjukkan oleh orang-orang yang lebih tahu.


10
2017-11-04 19:18