Overriding Admin shapes in custom Orchard CMS theme - admin

I'm doing a custom theme for Orchard CMS.
As part of client project one of the requirements is to have some extra functionality in blog admin page.
This is pretty easy doing some simple changes in Parts.Blogs.BlogPost.ListAdmin.cshtml
I do not want to change the Blog source code, I would like to override that wiew in theme just like I'm doing with all the others on front end.
Following some guidelines found on orchard forum I have tried the following paths:
~/Themes/MyTheme/Views/Parts.Blogs.BlogPost.ListAdmin.cshtml
~/Themes/MyTheme/Views/Orchard.Blogs/Parts.Blogs.BlogPost.ListAdmin.cshtml
~/Themes/MyTheme/Views/Dashboard/Admin/Parts.Blogs.BlogPost.ListAdmin.cshtml
but the view is not picked up.
So, How may I override a view in my theme that will be picked up by admin dashboard instead of the default one?
Thanks

You need to create a theme with a project file, then add a .cs file with something like this in it:
public class AdminOverride : IThemeSelector
{
public ThemeSelectorResult GetTheme(RequestContext context)
{
if (AdminFilter.IsApplied(context))
{
return new ThemeSelectorResult { Priority = 111, ThemeName = "NewAdminTheme" };
}
return null;
}
}
Don't set this theme as current though, just enable it from the backend. You will also need to set your TheAdmin as the base theme in the Theme.txt like this:
BaseTheme: TheAdmin

Related

Acumatica - How to change the page opened by the edit button (pencil)

I'm trying to customize a page so that the edit button (the pencil icon one) directs the user to a specific page. In my case, the edit button is linked to a BAccountID field :
By default, it opens the Business Account's page (page CR303000):
I would like it to open a different page that I created that has a similar BAccount view (page AR303001) :
Can this be done ? And how ? I can't seem to find the code behind this logic.
The target graph is declared in the PXPrimaryGraph attribute decorating the DAC.
BAccount DAC is a special case that uses a more complex attribute (CRCacheIndependentPrimaryGraphList) that inherits from PXPrimaryGraph.
[CRCacheIndependentPrimaryGraphList(new Type[]{
typeof(CR.BusinessAccountMaint),
typeof(EP.EmployeeMaint),
typeof(AP.VendorMaint),
typeof(AP.VendorMaint),
typeof(AR.CustomerMaint),
typeof(AR.CustomerMaint),
typeof(AP.VendorMaint),
typeof(AR.CustomerMaint),
typeof(CR.BusinessAccountMaint)},
new Type[]{
typeof(Select<CR.BAccount, Where<CR.BAccount.bAccountID, Equal<Current<BAccount.bAccountID>>,
And<Current<BAccount.viewInCrm>, Equal<True>>>>),
typeof(Select<EP.EPEmployee, Where<EP.EPEmployee.bAccountID, Equal<Current<BAccount.bAccountID>>>>),
typeof(Select<AP.VendorR, Where<AP.VendorR.bAccountID, Equal<Current<BAccount.bAccountID>>>>),
typeof(Select<AP.Vendor, Where<AP.Vendor.bAccountID, Equal<Current<BAccountR.bAccountID>>>>),
typeof(Select<AR.Customer, Where<AR.Customer.bAccountID, Equal<Current<BAccount.bAccountID>>>>),
typeof(Select<AR.Customer, Where<AR.Customer.bAccountID, Equal<Current<BAccountR.bAccountID>>>>),
typeof(Where<CR.BAccountR.bAccountID, Less<Zero>,
And<BAccountR.type, Equal<BAccountType.vendorType>>>),
typeof(Where<CR.BAccountR.bAccountID, Less<Zero>,
And<BAccountR.type, Equal<BAccountType.customerType>>>),
typeof(Select<CR.BAccount,
Where2<Where<
CR.BAccount.type, Equal<BAccountType.prospectType>,
Or<CR.BAccount.type, Equal<BAccountType.customerType>,
Or<CR.BAccount.type, Equal<BAccountType.vendorType>,
Or<CR.BAccount.type, Equal<BAccountType.combinedType>>>>>,
And<Where<CR.BAccount.bAccountID, Equal<Current<BAccount.bAccountID>>,
Or<Current<BAccount.bAccountID>, Less<Zero>>>>>>)
},
VerifyRightsBy = new [] { typeof(CR.BusinessAccountMaint) })]
There is no way to easily customize this attribute. To change it would you need to replace the BAccount DAC with another one. The preferred method for your use case is to avoid usage PXPrimaryGraph attribute by using a regular action button.
The action button can be configured to show the pencil icon:
Make PXButton appear as pencil icon
And it can be displayed beside the field using PXLayout Merge property or you can use LinkCommand to redirect:
https://stackoverflow.com/a/60446714/7376238

Using different page templates in TYPO3

I'm looking for some help about using different templates for each page. I'm using everything is on the TYPO3 video tutorial (linked below) but isn't explain there how to do what I need (which code must be write and where).
Site Package tutorial part 1
Site Package tutorial part 2
Site Package tutorial part 3
I suggest to start with the site package builder: https://sitepackagebuilder.com/ based on Bootstrap package which will a) bring you already helpful templates and b) shows you how to make your own ones (Example.html / Configuration/TsConfig/Page/Mod/Weblayout/BackendLayout.tsconfig).
Some helpful references: https://docs.typo3.org/typo3cms/SitePackageTutorial/FluidTemplates/Index.html
Connecting Fluid templates to Backend Layouts
So you have a special template for the about page and want to use it in TYPO3. You'll have to create a new Backend Layout for this template.
The Backend Layout could be configured in Page TSconfig like that:
mod.web_layout.BackendLayouts {
about {
title = About page
config {
backend_layout {
colCount = 1
rowCount = 1
rows {
1 {
columns {
1 {
name = main column
colPos = 0
}
}
}
}
}
}
icon = EXT:your_sitepackage/Resources/Public/Images/BackendLayouts/About.svg
}
}
In the next step, you'll have to connect your new Backend Layout with your template. This is done in TypoScript setup:
page = PAGE
page {
10 = FLUIDTEMPLATE
10 {
file.stdWrap.cObject = CASE
file.stdWrap.cObject {
// select a layout template depending on the page's BackendLayout:
key.data = pagelayout
// Important! If you set BackendLayouts through TSconfig, you MUST use the prefix 'pagets__':
pagets__1_column = TEXT
pagets__1_column.value = EXT:your_sitepackage/Resources/Private/Templates/1Column.html
pagets__about = TEXT
pagets__about.value = EXT:your_sitepackage/Resources/Private/Templates/About.html
default = TEXT
default.value = EXT:your_sitepackage/Resources/Private/Templates/1Column.html
}
layoutRootPaths {
10 = EXT:your_sitepackage/Resources/Private/Layouts/
}
partialRootPaths {
10 = EXT:your_sitepackage/Resources/Private/Partials/
}
variables {
}
}
}
Official video tutorial
See also the YouTube video about this topic: How to implement frontend layouts in TYPO3 using backend layouts
Finally, assign your new Backend Layout to single pages in TYPO3 backend
This is done in the page properties:
Open the page properties of a page in the TYPO3 Backend.
In the tab 'Appearance', you will find two options to assign Backend Layouts:
'Backend Layout (this page only)'
'Backend Layout (subpages of this page)'
These are pretty much self-explanatory:
The first option sets the desired Backend Layout only for this single page.
The second option will assign the Backend Layout for all sub pages of the current page. You can override this Backend Layout again: open the page properties of the sub page where you want a different layout and assign a new one.
On this website you can find an even more detailed explanation with screenshots (taken from the Backend of TYPO3 6.2).

Modifying the Wagtail publish dropdown (per app)

I'd like to reconfigure the default "Publish" menu. The default configuration is this:
I'd like to make Publish the default action, and move it to the top. I'd also like to remove Submit for Moderation, as our site has no current need for that feature.
Ideally, I'd love to be able to override the menu config on a per-app basis - we will likely have other sections of our site in the future where we want a different config.
Is this possible?
This isn't currently possible I'm afraid - the menu items are fixed in wagtailadmin/pages/create.html and edit.html.
This is possible as of Wagtail 2.4 using the register_page_action_menu_item hook, as per Yannic Hamann's answer. Additionally, Wagtail 2.7 (not released at time of writing) provides a construct_page_listing_buttons hook for modifying existing options.
You can add a new item to the action menu by registering a custom menu item with the help of wagtail hooks.
To do so create a file named wagtail_hooks.py within any of your existing Django apps.
from wagtail.core import hooks
from wagtail.admin.action_menu import ActionMenuItem
class GuacamoleMenuItem(ActionMenuItem):
label = "Guacamole"
def get_url(self, request, context):
return "https://www.youtube.com/watch?v=dNJdJIwCF_Y"
#hooks.register('register_page_action_menu_item')
def register_guacamole_menu_item():
return GuacamoleMenuItem(order=10)
Source
If you want to remove an existing menu item:
#hooks.register('construct_page_action_menu')
def remove_submit_to_moderator_option(menu_items, request, context):
menu_items[:] = [item for item in menu_items if item.name != 'action-submit']
The default button SAVE DRAFT is still hardcoded and therefore cannot be configured so easily. See here.
It seems it can't be done on server side without some monkey-patching.
However if you want it for yourself (or have access to computers of those who will publish) you can modify your browser instead.
Install Tampermonkey browser addon
Create new script with a content below
Change http://127.0.0.1:8000/admin/* to your wagtail admin panel url pattern
Save script and check admin panel
Result should look:
// ==UserScript==
// #name Wagtail: replace "Save draft" with "Publish"
// #match *://127.0.0.1:8000/admin/*
// #require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// ==/UserScript==
let $ = window.jQuery;
function modify() {
let draft = $("button.button-longrunning:contains('Save draft')");
let publish = $("button.button-longrunning:contains('Publish')");
if (draft.length && publish.length) {
swap(publish, draft);
}
};
function swap(a, b) {
a = $(a); b = $(b);
var tmp = $('<span>').hide();
a.before(tmp);
b.before(a);
tmp.replaceWith(b);
};
$(document).ready(function() {
setTimeout(function() {
try {
modify();
}
catch (e) {
console.error(e, e.stack);
}
}, 100);
});
Modifying the code above, these selectors works for every admin language:
let draft = $("button.button-longrunning.action-save");
let publish = $("button.button-longrunning[name='action-publish']");

Sitecore 8 MVC : How to make the field editable in Page Editor

I'm trying to make my Sitecore site compatible with page editor i.e. all the fields text, rich text editor, etc, should be able to be updated in Page Editor.
So far I've modified the TDS T4 template which returns a HTMLString for each field like below:
public HtmlString HeroImageField(bool isEditable = true, string parameters = "")
{
string renderParameter = GenerateRenderParameter(isEditable, parameters);
return new HtmlString(Sitecore.Web.UI.WebControls.FieldRenderer.Render(
Sitecore.Context.Database.GetItem(this.EntityId.ToString()), "Hero Image", renderParameter ));
}
So, in view I just call Model.HeroImage()
Is there a better way to achieve above? Maybe Glass Mapper comes with out of box support for this (which I don't know).
Sitecore comes with MVC helpers that you can use to make fields editable:
#Html.Sitecore().Field("My Field Name") //Context item by default
#Html.Sitecore().Field("My Field Name", Model.PageItem) //Explicit page item
#Html.Sitecore().Field("My Field Name", Model.Item) //Datasource item (may be context item)
My recommendation is also to use a controller to load a different view for editing. There is often a different visual needed for authoring fields. Carousels/tabs should be flattened out, validation messages need to be exposed, etc.
See more at:
http://sitecore-community.github.io/docs/sitecore-mvc/rendering-content/
Glass mapper has something OOTB for that. You can use the extended WebViewPage GlassView and use the Editable methods as described here:
http://glass.lu/Mapper/Sc/Tutorials/Tutorial14

Issue trying to use Datasource Template in Sitecore sublayout; getting empty Sublayout.Datasource

We've been trying to "componentize" our Sitecore solution as we move forward, in prep for transitioning to Page Editor usage (Woot! Finally!), but we're still practically working primarily with Page templates that may be inheritance-based composites of page specific fields, plus 1:many of these componentized templates. An example of how this looks in our solution is below -- Banner Feature Carousel and Featured Cartoon are some of these new components we're creating:
In the interest of trying to move away from using Sitecore.Context.Item (as I was recently reminded by this post) I've started filling in the Datasource template field on the sublayouts for the new components, and it seems like I've got the appropriate connections made between presentation details, the Sitecore sublayout and the .NET code file (as far as I can tell; again, we're newer to working this way).
I've also tried setting up a base class for these components as per this post by Nick Allen, but here's where I'm running into a problem: When I execute my code, this base class is finding the component Sublayout appropriately (the whole "this.Parent as Sublayout" thing) but, when I go to interrogate the Sublayout.Datasource property, it's an empty string. Here's my code (so far) for this base class:
public class ComponentBase : System.Web.UI.UserControl
{
private Sublayout Sublayout { get { return Parent as Sublayout; } }
public Item DataSourceItem
{
get
{
return Sublayout != null && !String.IsNullOrEmpty(Sublayout.DataSource) ?
Sitecore.Context.Database.GetItem(Sublayout.DataSource) : Sitecore.Context.Item;
}
}
}
I'm apparently missing some interplay between the Datasource Template field in the Sitecore sublayout, and how that actually translates to a datasource. Is it because these component templates are being used to compose Page templates? I was thinking that the datasource would just ultimately resolve to the Page template on which the component in question was currently being used, but perhaps that's my misunderstanding.
If anyone could give me any hints of things to check or point me to any resources I might use to get further, I'd appreciate it. I've done quite a bit of asking the Googs, myself, but am just not getting anything that's helping.
Thank you in advance, Sitecore friends!
It looks like you have configured the allowed templates for your sublayout in the steps you describe above. This basically tells Sitecore; 'allow user to select items based on these templates for this sublayout'. This alone does not set up the data source on items using the sublayout. You still need to go into the presentation details for any items using this sublayout, select the sublayout and then set its datasource property (within the content editor go to Presentation > Details > [Sublayout] > Data Source).
My answer to this question gives the source code needed to retrieve the datasource item and to iterate through the sitecore controls on your sublayout setting all of their Item propertys.
Here is the code:
public class SublayoutBase : UserControl
{
private Item _dataSource;
public Item DataSource
{
get
{
if (_dataSource == null)
{
if (Parent is Sublayout)
{
_dataSource =
Sitecore.Context.Database.GetItem(((Sublayout)Parent).DataSource);
}
if (_dataSource == null)
{
_dataSource = Sitecore.Context.Item;
}
}
return _dataSource;
}
}
protected override void OnLoad(EventArgs e)
{
foreach (Control c in Controls)
{
SetFieldRenderers(DataSource, c);
}
base.OnLoad(e);
}
private void SetFieldRenderers(Item item, Control control)
{
if (item != null)
{
var ctrl = control as Sitecore.Web.UI.WebControl;
if (ctrl != null && !string.IsNullOrEmpty(ctrl.DataSource))
{
//don't set the source item if the DataSource has already been set.
return;
}
if (control is FieldRenderer)
{
var fr = (FieldRenderer)control;
fr.Item = item;
}
else if (control is Image)
{
var img = (Image)control;
img.Item = item;
}
else if (control is Link)
{
var link = (Link)control;
link.Item = item;
}
else if (control is Text)
{
var text = (Text)control;
text.Item = item;
}
else
{
foreach (Control childControl in control.Controls)
{
SetFieldRenderers(item, childControl);
}
}
}
}
}
When you start using sublayouts on which you will set your datasource try to use the marketplace "Sublayout Parameter Helper". When you use the class they provide as base class and have set everything up right in your Sitecore Environment you will find yourself having a "this.DatasourceItem" -> which will contain your datasource Item, or the context Item if none is given.
http://marketplace.sitecore.net/en/Modules/Sub_Layout_Parameter_Helper.aspx
To find out more on how datasource works, try searching on http://sdn.sitecore.net. There is alot of documentation available that should get you in the right direction. Generally it will be enough to just add a datasource in the sublayout that you can access in your sublayout.
When you read a datasource from a sublayout all it will do is check if the datasource field on that sublayout is filled and will return you information regarding this datasource. So when you add a new component to a page using the page editor (for example a sublayout for a news article) and you have a datasource template defined and filled in during adding this components, you will see that when you check the presentation details on that page a sublyaout with a datasource has been added. The datasource will point to a folder in which you will find an item based on the datasource template you specified on your sublayout. You should also double check this, cause if no datasource is present on the added sublayout it will be obvious you can't access it from your code.
If you are using Sitecore 7 update 1 you can actually now use the attributes. From the Release Notes:
To make it easier to get the data source for a sublayout from
code-behind, the data source is now transferred to the sublayout
control in a "sc_datasource" attribute. (320768)
To get the data source from code, simply refer to
this.Attributes["sc_datasource"];