How can I best use the Tridion Broker as the single source of content for multiple web sites? - tridion

I am working on this Tridion implementation that has a bunch of very different websites, but some content (like news) is shared via the basic Tridion principle of blueprinting. Sites are all in the same language, so we are only dealing with branding differences.
Current situation: There is a publication called Global content in which this global content is created. In the schema there are some checkboxes where a child publication this content should appear can be selected. When the component is saved the event system kicks in and creates pages with the components on it, publishes it, etc... deletion of the components doesn't happen, only a resave with all checkboxes unchecked will eventually via a batch process remove pages.
Broker situation: I would like to start using the broker. More so, because in the future situation the website(s) will also start sharing more content to external websites, which I was going to do via RSS feeds or a basic API, which works best with content from the Broker.
The scenarios:
Allow this global content publication to publish dynamic content, and on the other sites pull that content straight from the Broker (with the Global Content Publication ID?)
Make a fake empty target in Global content so they can say "publish/unpublish to all child publications?" (You can still use the checkboxes to allow it to publish in certain publications)
Use a Global Content website for publishing dynamic content and create the API/RSS feeds for internal and external websites to use?
Something else?
My initial thought goes out to the first scenario, but I can see the main drawback that it would become more difficult to mix local(ized) news items and global news items.
The second scenario seems to be the second best chance. Anyone has experience with an implementation like that?

On the implementation I'm currently working on we are using something like the second solution. I added the website master publication (in which we create all pages) to the publication target we use for all the websites so we can use publish to all child publications from there. If it fits in your model I would prefer this option as it continues to give you the control over the items by localization in child publications.
Since we didn't feel like having the content rendered on the website master publication (as this isn't going anywhere and will just be a waste of my publisher processor time and then also a waste of broker storage when it gets deployed), we created a ChildOnlyPublicationResolver (SDL Tridion 2011). In this resolver we loop through all resolved items, and if the item comes from the website master publication, we remove it from the list.
The outcome is that you will see the website master publication appear in the publish queue, but it will be set to success almost instantaneously since there is nothing to render in it. So it doesn't take up any performance from the publisher nor is it deployed, but you keep the benefit of your child publications and have an easy way to publish them in one go.
If interested, here is an example for the resolver code:
using System.Collections.Generic;
using Tridion.ContentManager;
using Tridion.ContentManager.Publishing;
using Tridion.ContentManager.Publishing.Resolving;
namespace SDL.Example.Resolvers
{
public class ChildOnlyPublicationResolver : IResolver
{
/// <summary>
/// Master Publication TCMURI
/// </summary>
private const string MasterPublicationTcmUri = "tcm:0-2-1";
/// <summary>
/// For publish and unpublish, remove all items from the master publication from the list.
/// </summary>
/// <param name="item">Item to be resolved (e.g. a page, structure group, template)</param>
/// <param name="instruction">Resolve instruction</param>
/// <param name="context">Publish context</param>
/// <param name="resolvedItems">List of items that are currently to be rendered and published (added by previous resolvers in the chain)</param>
public void Resolve(IdentifiableObject item, ResolveInstruction instruction, PublishContext context, Tridion.Collections.ISet<ResolvedItem> resolvedItems)
{
List<ResolvedItem> itemsToRemove = new List<ResolvedItem>();
TcmUri masterPublicationUri = new TcmUri(MasterPublicationTcmUri);
// check for items from master publication (these do not need to be published or unpublished)
foreach (ResolvedItem resolvedItem in resolvedItems)
{
// mark all items from website structure publication for removal
if (resolvedItem.Item.Id.PublicationId == masterPublicationUri.ItemId)
{
itemsToRemove.Add(resolvedItem);
}
}
// remove all items that we need to discard
foreach (ResolvedItem itemToRemove in itemsToRemove)
{
resolvedItems.Remove(itemToRemove);
}
}
}
}

In a traditional Tridion architecture, your best bet might have been to inherit through the BluePrint. This would mean having the content available in all the broker in all publications, and determining which items to show from the metadata.
As Bart has suggested, there are some wasteful aspects to this design, so you might rather think in terms of the global content being "federated" from a single web site. This is what the Content Delivery web service is intended for. If you are on Tridion 2011, you can effectively choose your option 3, but with more out-of-the-box support than used to be present, so you wouldn't have to build the API, just consume it.

Related

Storing a view in Xamarin.Forms MvvmCross

I have an app with four main pages, switched through a tab bar (no "back" button).
One page has a lot of content (ScrollView) and takes quite a few seconds until it's rendered. I handle that by showing a "loading" overlay while the work is done. But for that specific page I'd like to keep the view alive, so that when the user switches to another page and comes back later, the page is ready without loading everything again.
I'm not sure how to do that in MvvmCross, though.
I did read the documentation and from what I understood the View Presenter would be the right way to do it, since the docs say:
"Another kind of presentation changes your app can request through
hints includes clearing / modifying the BackStack, changing a root
while maintaining the existent views, … possibilities are really
endless. Once again your app is king here!"
I guess I would need to create a custom MvxPresentationHint for that, but I don't quite get it :(
How or rather where would I access and store/load the View?
I'm generally still quite unfamiliar with MvvmCross (how it works under the hood) and especially customization of Mvx classes, even though I've been using it for a while.
Any explanation and preferably code examples beyond what's written in the documentation would be extremely appreciated!
It isn't meaningful to attempt to "store" a view in MVVM. The XF view is a representation of what will be created with native (e.g. "Android" or "iOS") widgets. Creating and measuring/laying out those native widgets is what is slow. MVVM View Presenter won't speed up that logic.
Instead of "store", you need "keep alive":
For a ContentPage called MyPage, when you create it, store it in a static variable. Then re-use that variable. If you never need more than one of these, you can store it in the class itself.
Modify the "code behind", MyPage.xaml.cs:
public partial class MyPage : ContentPage
{
// Singleton Pattern.
private static MyPage _it;
public static MyPage It {
get {
if (_it == null)
_it = new MyPage();
return _it;
}
}
// "private", because calling this directly defeats the purpose. Instead, use `MyPage.It`.
private MyPage()
{
InitializeComponent();
}
}
To create it, whereever you would put:
new MyPage()
instead put this:
MyPage.It
For instance, you might do PushAsync(MyPage.It);
This will always return the SAME INSTANCE of MyPage. So once it has been created, it keeps its state.
IMPORTANT: Note that the constructor is only called ONCE. Any code that needs to be done each time the page appears, put in override .. OnAppearing() method.
LIMITATION: Views "expect" to be part of the visual hierarchy when they are manipulated. If you attempt to alter the page or its view model while it is not on the screen, you may encounter problems. Those are beyond the scope of this answer - create a new StackOverflow question with the details of any problem you encounter.

How do I run code at client start (Not at page open) in Blazor

I need to run some code to get a user's username and department when they first connect to my Blazor Server Side application. I could just do this using OnInitialized() but that appears to only work on the one page in which it was placed. Users will likely be sent separate links to different pages though and I don't want to have to place this code on every page. I discovered that I can place code in my main layout and it will run no matter what page I start on but it runs on every page change and it doesn't allow me to run things asynchronously so that's not ideal. I'm looking for something like a Global.asax but in Blazor if that makes sense.
Edit: Turns out I can run things asynchronously in my layout! I just needed to create a code block like any other razor page. Makes sense. Though It's still weird that we have to put this type of code in the layout. It just doesn't feel right.
This is what I do:
Create a state object (class) that can be injected where needed. This is somewhat like session, but can also have global events. See here.
Add it to IoC in Startup.cs. Background info here
services.AddScoped<MyState>();
Initialize it in MainLayout.razor or elsewhere:
if (MyState.User == null)
{
MyState.User = authService.User;
}
Instantiate in pages/components as needed:
[Inject]
public MyState myState { get; set; }
...
myObj.CreatedBy = myState.User.UserName;

Update all links on page before returning page to client

Overview
We have an in house CMS that we've recently added multilingual support to. The CMS allows dragging/dropping of various panels (.net controls) and some panels show dynamic content entered via a rich text editor. Also, some fields are multilingual so some panel content will change according to the current language.
Ideally we want to add the language to the URL. So /contact-us becomes /en/contact-us.
Our main handler will then set the language and the all panels will show relevant copy.
Goal
So, ideally we'd like to be able to:
Process the page server side after it's been built by our main page assembler (eg in PreRender)
Parse the built page or recurse the control tree to update ALL internal links
Prepend a lanauge code to all internal links on the page. (easy enough once we know where they all are)
NB: Some links will by in .net HyperLink controls but others will be <a> tags entered via a Rich Text Editor.
Stuff I've looked at
I've skimmed google but haven't found anything that seems to match our needs:
Html Agility Pack - can be used to take a URL and parse for links. But I'm guessing this can't be used say in Pre_Render of our main page builder. Ideal for scraping I suppose.
Various JS solutions - locate links and update. Very easy but I'm wary of using JS for updating URLs client side.
All suggestions welcome :)
So, There will be dynamic content and static content. And the CMS users should be able to edit both of them. You should have a Language DB table, and for instance; For "about us" page, There should be about-us EN, about-us DE, about-us FR rows in other table.
And you should have another table for static content. For instance for contacy us form. There are static texts on contact forms. Name, e-mail,message etc.
This can be done by overriding Page.Render() as follows:
protected override void Render(HtmlTextWriter htmlWriter)
{
StringBuilder ThisSB = new StringBuilder();
StringWriter ThisSW = new StringWriter(ThisSB);
HtmlTextWriter RenderedPage = new HtmlTextWriter(ThisSW);
// pass our writer to base.Render to generate page output
base.Render(RenderedPage);
// get rendered page as a string
string PageResult = ThisSB.ToString();
// modify the page
string ModifiedPage = UpdatePage(PageResult);
// write modified page to client
htmlWriter.Write(ModifiedPage);
}
The method UpdatePage can manipulate the page as a string in any way you wish - in our case we use find and update all links and local file paths.

How do I control which linked Components are published when I publish a specific Component?

I am using SDL Tridion 2011 SP1. I have Components A, B and C. Component C is linked with A & B.
If I publish C, both Component A and B are getting published. But I want only Component A to be published.
Can any one explain how to exclude Component B from publishing?
What you are experiencing is the default behaviour of Tridion. This is by design, to ensure that when you change content in a component, publishing it will update all instances of that content on the website.
As the other answers suggest you can change this behaviour using a Custom Resolver:
using Tridion.ContentManager;
using Tridion.ContentManager.CommunicationManagement;
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.Publishing;
using Tridion.ContentManager.Publishing.Resolving;
public class UpdateResolvedItems : IResolver
{
public void Resolve(
IdentifiableObject item,
ResolveInstruction instruction,
PublishContext context,
Tridion.Collections.ISet<ResolvedItem> resolvedItems)
{
foreach (ResolvedItem resolvedItem in resolvedItems)
{
// Check resolved items, and remove accordingly
}
}
}
The code example above demonstrates you can get access to a collection called resolvedItems. This is a list of items due to be published, unless you make a change to it.
You can iterate through this list and remove items according to your requirements.
So far I know there is no easy way to do this. When you publish one item Tridion resolves all the related and linked items and publishes them.
You can use event system or a custom resolver to achive want you are asking.
This link might help:
http://nunolinhares.blogspot.com/2011/10/tridion-publisher-and-custom-resolvers.html
If you are publishing from API you can set IncludeComponentLinks property of ResolveInstruction to false, or, as Bappi pointed do this in the event handler

When DCPs are placed on a page in Tridion, how can you ensure that all the dynamic renderings of the component are published with the page?

Publishing a component which has multiple dynamic templates will usually result in all the possible dynamic component presentations being published to the broker.
When you create a DCT with the option to place the item on a page, a content editor may not want to publish the components directly, simply relying on the Page publish to do the right thing. We could consider three possible desired publishing scenarios:
That publishing the page should only cause the static component presentations to be rendered, (plus whatever CD code is necessary to display the dynamic ones)
That in addition to static CPs, any dynamic CPs should be published. Other possible dynamic renderings of the same component are not published.
If a dynamic CP is published, the usual component publishing semantics are followed,
and all dynamic renderings will go to the broker.
Tridion's default behaviour appears to be scenario 2), whereas my experience is that often what you want is scenario 3), giving you a complete and
consistent view of any given component on the CD side.
What is the best way to implement scenario 3 (including getting unpublish to work correctly)?
In my opinion, the best answer for your question is to implement a custom Resolver that would include the required Dynamic Component Presentations. I would be wary of doing anything when unpublishing, as sometimes you may want to keep the DCPs after unpublishing a given page (for "latest news" type of functionality or any other sort of dynamic queries), but the code sample below would make it simple for you to adapt if you need to unpublish all DCPs.
Warning: Below code is not production-tested.
using Tridion.ContentManager;
using Tridion.ContentManager.CommunicationManagement;
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.Publishing;
using Tridion.ContentManager.Publishing.Resolving;
public class IncludeDynamicComponentPresentations : IResolver
{
public void Resolve(
IdentifiableObject item,
ResolveInstruction instruction,
PublishContext context,
Tridion.Collections.ISet<ResolvedItem> resolvedItems)
{
if (!(instruction.Purpose == ResolvePurpose.Publish ||
instruction.Purpose == ResolvePurpose.RePublish))
{
// Do nothing more when unpublishing
return;
}
Session session = item.Session;
foreach (ResolvedItem resolvedItem in resolvedItems)
{
// Only do something if we're dealing with a page
if (!(resolvedItem.Item is Page)) continue;
Page page = (Page)resolvedItem.Item;
if (page.ComponentPresentations.Count > 0)
{
UsingItemsFilter filter = new UsingItemsFilter(session);
filter.InRepository = page.ContextRepository;
filter.ItemTypes = new[] { ItemType.ComponentTemplate };
foreach (ComponentPresentation cp in page.ComponentPresentations)
{
// Find all component templates linked to this component's schema
Schema schema = cp.Component.Schema;
foreach (ComponentTemplate ct in schema.GetUsingItems(filter))
{
if (!ct.Id.Equals(cp.ComponentTemplate.Id))
{
if (ct.IsRepositoryPublishable)
{
resolvedItems.Add(new ResolvedItem(cp.Component, ct));
}
}
}
}
}
}
}
}
You would now need to add this to the GAC and modify [Tridion]\Config\Tridion.ContentManager.Config so this Resolver is called after every resolve action (under resolving/mappings for every item type).
Perhaps a Custom Resolver would help in this situation? This would give you access to all the items the result from a publish action, allowing you to change the default behaviour.
There's a good example of this in the SDL Tridion documentation portal, but it basically allows you to create a custom resolver class in .net, where you can implement your custom logic.

Resources