I am stuck with this problem for nearly one day now:
In an application, I create a publishing page in code:
PublishingPage newPage = pages.Add(usableName, layout);
newPage.ListItem["Title"] = promoRecord.PromotionName;
newPage.ListItem["Description"] = string.Empty;
newPage.Update();
newPage.CheckIn("First draft");
So far so good. The problem is, I need the newly created page to appear at the top of the navigation. I was naive enough to think something as simple as this:
SPNavigationNodeCollection navigationNodes = pWeb.CurrentNavigationNodes;
SPNavigationNode newNode = null;
foreach (SPNavigationNode node in navigationNodes)
{
if (node.Url.Equals(prefix + newPage.Url, StringComparison.OrdinalIgnoreCase))
{
newNode = node;
}
}
newNode.MoveToFirst(navigationNodes);
would work. It doesn't, because the page is simply not there (in the CurrentNavigationNodes collection).
So I tried with:
newNode = new SPNavigationNode(promoRecord.PromotionName, prefix + newPage.Url);
navigationNodes.AddAsFirst(newNode);
with no luck either - here I got an exception saying that I can't add the page because it's in DRAFT state. Actually, the CurrentNavigation seems to get updated when I go to the frontend management (Manage Content And Structure / Site Administration / Navigation) - and the page appears there. Even if it's in DRAFT mode.
I tried a lot of things with no success... maybe you guys have an idea what I could try?
Thanks a lot in advance!
Related
I need a piece of advice.
We have an existing system based on .Net core/MVC. And we're moving all frontend parts to Blazor. For now we have one part where we're not sure how to better solve the task.
We have a module, which controls rooms. It's a web page which loads an SVG scheme of a floor from the database. Each room in SVG is an < G > element with ID. Like < G room-id="15" >...
In the existing app, we have a javascript code, which runs after the page is loaded and this script appends to each element an "onclick" event with "room-id" parameter.
Also this script changes fill color of the element if there is no "room-id" to show inactive rooms.
Now, we have to move these functions to Blazor. But Blazor can't manipulate DOM directly. We can keep Javascript, of course. But it would be the worst solution.
We're ready to update backend as well. For example, one of our ideas is to use HtmlAgility package, parse SVG (which is XML by fact), and insert CSS class to inactive rooms. By this way we can solve our second problem (Maybe).
With OnClick events the only idea is again to parse SVG inside the Blazor component, and then rebuild the picture with code and add needed events.
But maybe somebody could find a better way to do these tasks.
Thanks a lot!
Dmitry
Finally, how I solved the task.
SVG elemens have options like "onMouseOver", "onClick" and so on.
So, couldn't completely avoid Javascript. But it's now minimal and easy to maintain.
xScheme = XDocument.Parse(roomScheme);
elements = xScheme.Descendants("{http://www.w3.org/2000/svg}g");
foreach (var el in elements)
{
var roomId= el.Attribute("data-room-id")?.Value;
if (roomId!= null)
{
Guid.TryParse(roomId, out var roomGuid);
var room = allRooms.FirstOrDefault(s => s.Id == roomGuid);
if (room != null)
{
el.SetAttributeValue("class", "thover");
el.SetAttributeValue("title", $" ...Hint details ...");
el.SetAttributeValue("data-placement", "top");
el.SetAttributeValue("data-html", "true");
var attr = new XAttribute("onClick", $"window.location.href = '/RoomDetails/{roomGuid}'");
el.Add(attr);
attr = new XAttribute("onMouseover", " $(function() {$('.thover').tooltip();});");
el.Add(attr);
}
else
{
el.SetAttributeValue("class", "inactive");
}
}
}
And then inside Razor part:
#((MarkupString)xScheme.ToString())
I was wondering what is the best way to delete all pages in Sitefinity?
I came up with two solutions but I'm not sure what the best practices are.
I also read the documentation fore deleting pages.
The goal is to delete hierarchical pages as well.
Fluent
var fluent = App.WorkWith().Pages();
fluent.LocatedIn(Telerik.Sitefinity.Fluent.Pages.PageLocation.Frontend).Delete().SaveChanges();
Native
var pageManager = PageManager.GetManager();
var pageNodes = pageManager.GetPageNodes().ToList();
foreach (var node in pageNodes)
{
pageManager.DeleteItem(node);
}
pageManager.SaveChanges();
P.S. I was using the Fluent approach but after a while an error started to pop up. I switched to PageManager approach but I get the same error. I deleted all pages from the backend and the recycle bin, but still no resolution.
No row for Telerik.Sitefinity.Pages.Model.PageNode ('sf_page_node')
GenericOID#4f0f7ba8 PageNode id=6bd454ba-6971-4289-822d-36fbd9f5a844
NOTRES
Edit: The pages are deleted despite the error.
You should be very specific when deleting pages as the native version you are using will pull all pages including BackendPages. I have deleted ALL the pages in a Sitefinity site before.
According to the docs they use a slightly different approach for deleting pages.
https://docs.sitefinity.com/for-developers-delete-pages#delete-a-page-using-native-api
This deletes by title but can be easily modified to suit you. The docs also recommend unpublishing the page before deletion too.
public void DeletePageNativeAPI(string pageTitleToDelete)
{
PageManager pageManager = PageManager.GetManager();
PageData page = pageManager.GetPageDataList().Where(pD => (pD.NavigationNode.Title == pageTitleToDelete && pD.Status == ContentLifecycleStatus.Live)).FirstOrDefault();
if (page != null)
{
pageManager.Delete(page);
pageManager.SaveChanges();
}
}
Pages consist of a Template > PageNode > PageData
https://docs.sitefinity.com/for-developers-crud-operations-with-pages#page-components
Depending of which version you are working with of Sitefinity pages are a little different since the addition of Personalization and such.
It does not really matter which way you go (Fluent vs Native) - the end result should be the same. It is just a matter of coding style and readability.
I like Native more because I feel I have more control :)
As for the error - when using the PageManager - have you tried
pageManager.Delete(node) as opposed to the pageManager.DeleteItem(node)?
They seem slightly different.
How many pages do you have to delete?
If they are thousands, you can add a counter and call pageManager.SaveChanges() on every 100 or so deletions so that the transaction is committed to the DB.
Background
We have a custom developed installed .WSP on a SharePoint 2007 environment and have been in the process of upgrading to 2010. With the upgrade the custom event trigger no longer worked so trying to update and make it work in 2010. But I am running into one issue. Original developers no longer here and I've been the lucky one to have to figure this one out without much of a background with SP Dev.
Goal
When a new list item is created trigger event. Within event, create a shared folder using Item Name and return url, create a wiki-page using item name and include shared document link and return url to wiki page. Part three is update newly created list item with the New Folder url and Wiki Page URL.
Issue
I've gotten the first two parts working but so far have been unable to update the newly created list item with the new Links. I'm able to get the links. I've tried all the basic stuff for updating the list that I have been able to find online with no luck. Nothing to complicated(or so I think). But code is included below. VS is not installed on the server so unable to run debug mode, I don't have direct access to the server. When you create the item there are no client/user side error. Haven't been able to find a log file that has any, that is if it collects errors if the script were to fail out.
Initiation of the Event
public class CreateWikiAndFolder : Microsoft.SharePoint.SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
try
{
//this.DisableEventFiring();
base.EventFiringEnabled = false;
string sUrlOfWikiPage = string.Empty;
string sUrlOfNewFolder = string.Empty;
string sSubsiteRUL = string.Empty;
string sCurrentItemTitle = properties.ListItem["Title"].ToString();
string sWikiListName = "TR Wikis";
string sDocLibName = "Shared Documents";
string sTRListID = "TR Status";
if (sTRListID.ToUpper().Equals(properties.ListTitle.ToString().ToUpper()))
{
//Create the Folder
sUrlOfNewFolder = CreateFolder(properties.ListItem.Web, sDocLibName, sCurrentItemTitle);
//Create the Wiki
string ItemDispFormUrl = String.Concat(properties.ListItem.Web.Url, "/", properties.ListItem.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, "?ID=", properties.ListItem.ID.ToString());
sUrlOfWikiPage = CreateWiki(properties.ListItem.Web, sWikiListName, sCurrentItemTitle, ItemDispFormUrl, sUrlOfNewFolder);
//Update the current TR Item
//Have tried. properties.ListItem["WikiURL"] = sUrlOfWikiPage + ", " + "Wiki";
SPListItem myListItem = properties.ListItem;
SPFieldUrlValue shareFolderURLValue = new SPFieldUrlValue();
shareFolderURLValue.Description = "Shared Folder";
shareFolderURLValue.Url = sUrlOfNewFolder ;
myListItem["SharedFolder"] = shareFolderURLValue;
//I've tried each one separate and together to no luck
myListItem.UpdateOverwriteVersion();
myListItem.Update();
//properties.ListItem.UpdateOverwriteVersion();
}
base.EventFiringEnabled = true;
}
}
}
Note that this is the last thing needed to be figured out for our upgrade.
Got it working. I did both of these at the same time so I'm not sure if it was the combination of both or only one of the items. But one I removed the myListItem.UpdateOverwriteVersion(); line and surrounded the item updated with web.AllowUnsafeUpdates being set to true before and then back to false afterwards.
Also as a note to others, you need to save the properties.ListItem to its own SPListItem which you then update versus trying to manipulate the values at the properties.ListItem["Attribute"], and then update the properties.ListItem.Update. SharePoint doesn't allow the latter option so you have to save to an independent SPListItem, and then modify and update that one. This might not be the best SharePoint lingo, but that is what needs to be done.
I'm playing with SharePoint 2010 now and have a problem.
There is a feature that is responsible for webparts - it's scope is web. It's needed to update some properties of already created webparts, for example - title.
So I've overridden FeatureUpgrading event and added custom upgrade action into feature manifest - there is no problem here.
In that feature receiver I plan to have a code that should get the file with needed page, check it out, iterate through all the web parts on it, change property and then check in page back.
The problem is that all my webparts appear as ErrorWebPart with empty title.
By the way, if I use the same code in FeatureDeactivating event - everything works good.
The only difference I've noticed - in featureupgrading HttpContext.Current was null. So I've populated it manually but it didn't help.
While googling, the only two advices were: populate HttpContext and ensure that libs with webparts are in GAC. Both conditions are done in my situation.
The sample code from FeatureUpgrading is as proper:
SPUtility.ValidateFormDigest();
web.AllowUnsafeUpdates = true;
var request = new HttpRequest("", web.Url, "");
HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
var fileUrl = web.Url + "/pages/sample.aspx";
var file = web.GetFile(fileUrl);
if (file.CheckOutType == SPFile.SPCheckOutType.None)
{
file.CheckOut();
}
using (var webPartsManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
foreach (WebPart webPart in webPartsManager.WebParts)
{
Console.WriteLine(webPart.GetType().ToString());
}
}
file.CheckIn("System update");
Would appreciate any help. Maybe there is something I'm missing or there is another variant to accomplish described task?
Regards.
Since it is working on Deactivating(), I think the order in which the features are activated is the problem.
You may have to ensure the below:
1. First activate the Feature that adds the webparts
2. Then activate the new feature.
You can change this in package. Or better add a dependency to your new feature.
As part of a sandbox solution I am creating, I have a web provisioned event receiver that does the following:
Updates the new sites branding to match the root site
Updates the default sitepages/home.aspx (if it exists)
Deletes the OOTB default.aspx (if the site has a sitepages/home)
This all seems to work ‘most’ of the time, but occasionally when creating a sub site the following error message appears:
Error - The file SitePages/Home.aspx has been modified by xxx#xxx on 02 Oct 2012 08:51:36 -0700.
It doesn’t happen all the time which makes it really strange to understand and debug. It almost appears to happen if you create a site to quick after creating another?
Can anyone help me understand why this might be happening. It is worth noting that I am on SharePoint Online so can not check the correlation ID.
public override void WebProvisioned(SPWebEventProperties properties)
{
// Get and set child and top sites
SPWeb childSite = properties.Web;
SPWeb topSite = childSite.Site.RootWeb;
// Apply branding from top site to childsite
childSite.MasterUrl = topSite.MasterUrl;
childSite.CustomMasterUrl = topSite.CustomMasterUrl;
childSite.AlternateCssUrl = topSite.AlternateCssUrl;
childSite.SiteLogoUrl = topSite.SiteLogoUrl;
childSite.Update();
// Construct HTML for new home.aspx page
string content = "Test Content";
// Check if the newsite has a sitepages library and home.aspx
SPFile oFile = childSite.GetFile("Sitepages/Home.aspx");
if (oFile.Exists)
{
// replace page content with new html
oFile.Item["WikiField"] = content;
// Update
oFile.Item.Update();
// Delete old Default page.
SPFile oDefault = childSite.GetFile("default.aspx");
if (oDefault.Exists)
{
oDefault.Delete();
}
oDefault.Update();
}
}