예제 #1
0
        // CRUD editor for simple POCO with recursive sub collections
        public static void AddPersonCollection(this ICmsConfig config)
        {
            config.AddCollection <Person, BaseRepository <Person> >("person", "Contact", "Red20", "People", collection =>
            {
                collection
                .SetTreeView(x => x.Name)
                .SetElementConfiguration(
                    x => x.Id,
                    x => x.Name,
                    x => x.Details != null ? x.Details.Email : "")
                // this repository handles all the CRUD for this collection
                // a list view is a table that displays a row (or multiple rows) of info per entity
                .SetListView(view =>
                {
                    // if new entities can be made using the CMS, include a New button so users can insert new entities
                    view.AddDefaultButton(DefaultButtonType.New);

                    // multiple rows can be added to display even more data
                    // only the first row will be used to generate headers
                    view.AddRow(row =>
                    {
                        // views always require you to specify strings, so the Id (an int) must be .ToString-ed.
                        // since this messes up the name of the column, you can set it to a nice name
                        row.AddField(p => p.Id.ToString()).SetName("ID");
                        row.AddField(p => p.Name);

                        // if an entity can be edited, include an Edit button so users can start editing entities
                        // the edit button in a list will always direct the user to the NodeEditor
                        row.AddDefaultButton(DefaultButtonType.Edit);
                    });
                })
                // a list editor is similar to a list view, but every column contains an editor so multiple entities can be edited in one go
                // a list editor takes precedence over a list view, so when navigating to the Person collection, this view will be displayed
                .SetListEditor(editor =>
                {
                    editor.SetPageSize(20);

                    // adding Up to the button bar allows the user to get to the level above the current page (base upon the tree)
                    // this button will be hidden automatically when the user is at the root
                    editor.AddDefaultButton(DefaultButtonType.Up);

                    // in a list editor a New allows the user to add entities, within the list editor
                    editor.AddDefaultButton(DefaultButtonType.New);
                    editor.AddDefaultButton(DefaultButtonType.Return);

                    // adding a SaveExisting button to the ListEditor allows the user to bulk-save the entire list
                    // (only modified entities are touched)
                    editor.AddDefaultButton(DefaultButtonType.SaveExisting);

                    // allowing reordering so the user can shuffle the entities around and save them in a new order.
                    // the Repository must implement ReorderAsync
                    editor.AllowReordering(true);

                    // a list editor can be in the shape of a table, or a set of blocks, so these sections are either rows or blocks
                    editor.AddSection(row =>
                    {
                        // these fields will be the editors
                        row.AddField(p => p.Id).SetType(EditorType.Readonly);
                        row.AddField(p => p.Name);

                        // the SaveExisting button is only displayed when an entity is edited
                        row.AddDefaultButton(DefaultButtonType.SaveExisting, isPrimary: true);
                        // the SaveNew button is only displayed when an entity is inserted
                        row.AddDefaultButton(DefaultButtonType.SaveNew, isPrimary: true);

                        // the View button always directs the user to the NodeView
                        row.AddDefaultButton(DefaultButtonType.View);
                        // the Edit button always directs the user to the NodeEdit
                        row.AddDefaultButton(DefaultButtonType.Edit);
                    });
                })
                .SetNodeEditor(editor =>
                {
                    // adding Up to the button bar allows the user to get to the level above the current page (base upon the tree)
                    editor.AddDefaultButton(DefaultButtonType.Up);

                    // just as in the ListEditor, SaveExisting only shows when the user is editing an existing entity,
                    // and the SaveNew only when inserting an entity
                    editor.AddDefaultButton(DefaultButtonType.SaveExisting, isPrimary: true);
                    editor.AddDefaultButton(DefaultButtonType.SaveNew, isPrimary: true);

                    // if an entity can be deleted, add a button for it, so the user can delete the entity
                    editor.AddDefaultButton(DefaultButtonType.Delete);

                    // an node editor can have multiple sections, which are displayed as separate blocks
                    editor.AddSection(section =>
                    {
                        // the DisableWhen expression is evaluated every time any property of the entity is updated
                        // so this allows you to make response forms which show or hide parts based upon the entity and its state
                        section.AddField(x => x.Id).DisableWhen((person, state) => true);

                        // it is allowed to use DisplayType fields in Editors, so some read only data can easily be displayed
                        section.AddField(x => x.Name);     //.SetType(DisplayType.Label);

                        // if properties are in nested objects (like owned entities in EF), the editors support those as well
                        // flagging such property with [ValidateObject] will have the nested object validated as well
                        section.AddField(x => x.Details.Email)
                        // by default, all the properties will be combined to form the Name of the property (DetailsEmail in this example)
                        // so using SetName this can be set to something more user friendly
                        .SetName("Email")
                        .SetDetails(new MarkupString("<p>An email adress looks like <code>[email protected]</code>.</p>"));
                    });

                    // you can even have sections specifically for an entity type.
                    // if the repository can return multiple types of entities (all derived from a shared base type),
                    // sections can be made specifically for a type
                    editor.AddSection <Person>(section =>
                    {
                        // sections can have labels to make complex forms more understandable
                        section.SetLabel("Biography");

                        // sections can be hidden using VisibleWhen, based upon the entity or the state of that entity
                        // so users won't be confronted with editors that do not apply
                        section.VisibleWhen((person, state) => state == EntityState.IsExisting);

                        // there are many types of editors available, and even custom types can be used
                        section.AddField(x => x.Details.Bio).SetType(EditorType.TextArea).SetName("Bio");
                    });

                    editor.AddSection(section =>
                    {
                        // relations with other entities, collections and repositories are first-class in RapidCMS
                        // so this field will allow the user to select an entity that is one level deeper in the person-tree
                        section.AddField(x => x.FavouriteChildId)
                        .SetName("Favorite child")
                        .SetType(EditorType.Select)
                        .VisibleWhen((person, state) => state == EntityState.IsExisting)
                        .SetCollectionRelation <Person>("person", config =>
                        {
                            // this allows for configuring which property of the entity will make up the id for the element, and that value
                            // is set to FavouriteChildId when the user selects an element
                            config.SetElementIdProperty(x => x.Id);

                            // because a single label is often not enough, you can add more properties to make the labels for each option
                            config.SetElementDisplayProperties(x => x.Name, x => $"{x.Id} - {x.Details.Email}");

                            // sets the entity that is currently edited as parent for the repository to get elements for this field
                            config.SetEntityAsParent();

                            // any level from the tree can be picked:
                            // Favorite sibling: config.SetRepositoryParent(x => x);
                            // Favorite parent: config.SetRepositoryParent(x => x.Parent);
                            // Favorite grand parent: config.SetRepositoryParent(x => x.Parent != null ? x.Parent.Parent : default); // ?. is not yet supported in expressions..
                        });
                    });

                    // add the sub collection as element to the node editor
                    editor.AddSection(section =>
                    {
                        section.AddSubCollectionList("person");
                    });
                })
                .SetNodeView(view =>
                {
                    view.AddDefaultButton(DefaultButtonType.SaveExisting, isPrimary: true);
                    view.AddDefaultButton(DefaultButtonType.SaveNew, isPrimary: true);

                    view.AddDefaultButton(DefaultButtonType.Delete);

                    view.AddSection(section =>
                    {
                        // views also allow for customization of how the data should be displayed
                        // you can use the available DisplayType's, or create your own Razor components (must be derived from BaseDisplay)
                        section.AddField(x => x.Id.ToString()).SetName("ID").SetType(DisplayType.Pre);
                        section.AddField(x => x.Name).SetType(DisplayType.Label);
                        section.AddField(x => x.Details.Email).SetType(typeof(EmailDisplay));
                    });

                    view.AddSection(section =>
                    {
                        section.AddField(x => x.Details.Bio);
                    });
                });

                collection.AddSelfAsRecursiveCollection();

                // if the regular node editor of an entity is getting too cluttered, or if you want to move some of the functionality
                // to a separate page, a detail page can be quite useful.
                // a detail page is always a NodeEditor, and is only visible on existing entity.
                // the repository it fetches its data from can have its own entity type, but the ID that is used to query for the data
                // is the same as the entity the page is part of.
                // it is also possible to create a navigation button to navigate to a details page, by building a INavigationHandler that
                // calls NavigationRequest.NavigateToDetails (see NavigateToPersonHandler)
                collection.AddDetailPage <Details, BaseRepository <Details> >("person-details", "Settings", "Red20", "Details", config =>
                {
                    config.AddDefaultButton(DefaultButtonType.SaveExisting, "Save");

                    config.AddSection(section =>
                    {
                        section.AddField(x => x.Title);
                        section.AddField(x => x.History);

                        // when a model has a sub class which has a few simple properties, it's possible to group those properties
                        // into a single editor using the ModelEditor type. this editor uses the same rules as the convention based
                        // approach (see ConventionCollection), but groups the editors together of this property.
                        //
                        // to validate the properties of the nested object, the Nested property must be attributed with [ValidateObjectAsProperty]
                        // to instruct the data annotation validator to validate NestedDetails and use that validation as validation result
                        // for Nested.
                        section.AddField(x => x.Nested)
                        .SetType(EditorType.ModelEditor);
                    });
                });
            });
        }