Adding blogposts with categories/tags programatically
I'm trying to programmatically add blog posts complete with categories and tags but i'm coming up against several issues. I have managed to add the blog posts to the database, and the taxa are added, but do not seem to be associated correctly.
In the front end:
the blog post displays in the list of posts, but clicking on the title results in the error:
[NoSuchObjectException: No row
for
Telerik.Sitefinity.Blogs.Model.BlogPost (
'sf_blog_posts'
) GenericOID@44441b29 BlogPost content_id=35da53b0-fa9e-4862-beb7-8b6f05f8dbd7 NOTRES ]<br> DynamicModule.ns.Wrapped_OpenAccessBlogProvider_fa5ec75407c547f4bd5e4f613fb812ca.GetItemFromUrl(Type itemType, String url, Boolean published, String& redirectUrl) +351<br> Telerik.Sitefinity.Modules.GenericContent.ContentManagerBase`1.GetItemFromUrl(Type itemType, String url, Boolean published, String& redirectUrl) +75<br> Telerik.Sitefinity.Web.UI.ContentUI.ContentView.ResolveDetailItemFromUrl() +240<br> Telerik.Sitefinity.Web.UI.ContentUI.ContentView.ResolveDetailItem() +750<br> Telerik.Sitefinity.Web.UI.ContentUI.ContentView.CreateChildControls() +63<br> System.Web.UI.Control.EnsureChildControls() +102<br> System.Web.UI.Control.PreRenderRecursiveInternal() +42<br> System.Web.UI.Control.PreRenderRecursiveInternal() +175<br> System.Web.UI.Control.PreRenderRecursiveInternal() +175<br> System.Web.UI.Control.PreRenderRecursiveInternal() +175<br> System.Web.UI.Control.PreRenderRecursiveInternal() +175<br> System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2496
var categoryList =
new
List<Guid>();
foreach
(
string
str2
in
strArrays1)
TaxonomyManager tm = TaxonomyManager.GetManager();
HierarchicalTaxonomy ct = tm.GetTaxonomies<HierarchicalTaxonomy>().Where(t => t.Name ==
"Categories"
).SingleOrDefault();
if
(ct !=
null
)
Guid txid;
Taxon tx = ct.Taxa.Where(b => b.Title.ToLowerInvariant() == str2.ToLowerInvariant()).FirstOrDefault();
if
(tx ==
null
)
txid = Guid.NewGuid();
tx = (Taxon)tm.CreateItem(
typeof
(HierarchicalTaxon), txid);
tx.Taxonomy = ct;
tx.Title = str2.ToLowerInvariant();
tx.Name = str2.ToLower();
tx.Description =
"Represents a blog category"
;
tx.UrlName = HttpUtility.UrlEncode(str2.ToLowerInvariant());
ct.Taxa.Add(tx);
tm.SaveChanges();
else
txid = tx.Id;
categoryList.Add(txid);
var keywordList =
new
List<Guid>();
foreach
(
string
str2
in
strArrays1)
TaxonomyManager tm = TaxonomyManager.GetManager();
FlatTaxonomy ct = tm.GetTaxonomies<FlatTaxonomy>().Where(t => t.Name ==
"Tags"
).SingleOrDefault();
if
(ct !=
null
)
Guid txid;
Taxon tx = ct.Taxa.Where(b => b.Title.ToLowerInvariant() == str2.ToLowerInvariant()).FirstOrDefault();
if
(tx ==
null
)
txid = Guid.NewGuid();
tx = (Taxon)tm.CreateItem(
typeof
(FlatTaxon), txid);
tx.Taxonomy = ct;
tx.Title = str2.ToLowerInvariant();
tx.Name = str2.ToLower();
tx.Description =
"Represents a blog Tag"
;
tx.UrlName = HttpUtility.UrlEncode(str2.ToLowerInvariant());
ct.Taxa.Add(tx);
tm.SaveChanges();
else
txid = tx.Id;
keywordList.Add(txid);
var parentID =
new
Guid(
"79525FB5-AD2D-4931-9055-53382996F186"
);
var fluent = App.WorkWith();
Guid DynamicGuid = Guid.NewGuid();
var blog = fluent.Blog(parentID)
.Get();
fluent.BlogPost().CreateNew()
.Do(bp =>
bp.Parent = blog;
bp.Title = strTitle;
bp.Description = strDescription;
bp.Content = strBody;
bp.DateCreated = dtDateCreated;
bp.PublicationDate = dtDateCreated;
bp.AllowComments =
false
;
bp.ApproveComments =
true
;
bp.AllowTrackBacks =
true
;
bp.Summary = strDescription;
bp.PostRights = PostRights.None;
if
(categoryList.Count > 0)
foreach
(Guid a
in
categoryList)
bp.Organizer.AddTaxa(
"Category"
, a);
if
(keywordList.Count > 0)
foreach
(Guid a
in
keywordList)
bp.Organizer.AddTaxa(
"Tags"
, a);
)
.Publish()
.SaveChanges();
Okay, i've figured a couple more things out:
Neither categories nor tags work if there are disallowed characters in the UrlName field - I've been URLEncoding the title to create this field, and it's obviously not correct. If I remove the invlaid characters ("+") then it does display the correct posts.
The error displaying a single blog post is because the link in the title is doubling up the baseUrl - it is looking for:
/news/News/...
How can I fix this?
Thanks
Ryan
Another thing i've found is that the programmatically added blog posts do not have an id, just a contentid. If I try to set this ID while creating the blogpost, I get the error Change of Identity is not supported.
Hello Ryan,
Here is a list with all problems you are describing and answer to each item. Most of the problems I did not reproduce while running the sample for creating blog post and assigning a tag/category to it.
Please, can you comment each problem how to reproduce it ?
1)Front end:
the blog post displays in the list of posts, but clicking on the title results in the error:
Not reproduced.
2) "In the backend:
Blog posts are being added in DRAFT status, even though I call Publish() when creating them. "
This is a known issue and it will be and fixed in one of the next internal builds.
3) "Edit a post, click on publish, get the error message:
The input sequence contains more than one element"
Not reproduced
4) "Try to delete the post, get the error message:
Workflow rules do not allow to delete"
Not reproduced. This error appears when the user you use does not have permissions to manage content items through the workflow.
5)
"Neither categories nor tags work if there are disallowed characters in the UrlName field - I've been URLEncoding the title to create this field, and it's obviously not correct. If I remove the invlaid characters ("+") then it does display the correct posts."
Instead of using UrlEncode you can use regular expression to replace not allowed symbols:
const string UrlNameCharsToReplace = @"[^\w\-\!\$\'\(\)\=\@\d_]+";
const string UrlNameReplaceString = "-";
tx.UrlName = Regex.Replace(str2.ToLowerInvariant(), UrlNameCharsToReplace, UrlNameReplaceString);.
6) "Another thing i've found is that the programmatically added blog posts do not have an id, just a contentid. If I try to set this ID while creating the blogpost, I get the error Change of Identity is not supported."
Thanks Milena,
Most of the issues have been sorted out now that the Taxa are being created correctly
1), 3), 4), 5), 7) Resolved when Taxa are created correctly, using the above regex to clean the UrlNames.
2) Resolved by adding bp.ApprovalWorkflowState = "PUBLISHED"
6) contentid is a guid, id is NULL. Still happening, occurs only when creating blogposts programmatically, does not occur using the backend page.
Additional issues:
8) The doubled up page name in the Urls generated for the blog posts is still an issue. The page name is News, seems to be adding the page name twice to the UrlName of the blogpost. Happens on both programmatically added blogposts and using the backend. Is this being pulled from the page name, or is this a hardcoded value in the blogs module?
/news/News/2011/05/31/tough-new-law-on-careless-driving
9) The blog posts are being created with the current datetime, not the supplied value. I'm assuming this is because calling Publish() changes the dates to the current datetime. I've noted that changing the publication_date, date_created and last_modified in the database results in errors in the front end, it is unable to find the blog post unless the current date is used in the URL. This is a major issue because i'm importing about 4 years of daily blog posts.
/news/News/2011/05/31/tough-new-law-on-careless-driving <=======works
/news/News/2008/08/14/tough-new-law-on-careless-driving <======= cannot be found
Small issue - bp.AutoGenerateUniqueUrl is documented as "Gets or Sets a value..." but is read-only.
Ryan
Figured out 8), but it is still an issue. The first "news" is the "Default page"
that the blog is on, the second "News" is the "Blog Url". I'm unable to add an empty Blog Url field to resolve this as it is a required field.
Is there any reason for requiring both of these fields? Just seems a bit pointless.
Hi Ryan,
About point 9) you can set publication date using for setting publication date:
var publicaitonDate = DateTime.Now.AddYears(-2); //set date in the past
fluent.BlogPost()
.CreateNew()
.Do(bp =>
postId = bp.Id;
bp.Parent = blog;
bp.Title = strTitle;
//bp.Description = strDescription;
//bp.Content = strBody;
bp.DateCreated = publicaitonDate;
// bp.PublicationDate = publicaitonDate;
bp.AllowComments = false;
bp.ApproveComments = true;
bp.AllowTrackBacks = true;
//bp.Summary = strDescription;
//bp.PostRights = PostRights.None;
if (categoryList.Count > 0)
foreach (Guid a in categoryList)
bp.Organizer.AddTaxa("Category", a);
if (keywordList.Count > 0)
foreach (Guid a in keywordList)
bp.Organizer.AddTaxa("Tags", a);
)
.Publish()
.SaveChanges();
var blogPost = fluent.BlogPost(postId)
.CheckOut()
.Do(n => n.PublicationDate = publicaitonDate; n.DateCreated = publicaitonDate; )
.CheckIn()
.Publish()
.Do(n => n.PublicationDate = publicaitonDate; n.DateCreated = publicaitonDate; )
.SaveChanges();
I will continue to work on point 6) and 8)
Let me know if provided code do not solve issue 9).
Thanks!
Hello Ryan,
About 6) Can you send an example how to reproduce this problem ? When the post Id is null?
About 8) As you figured out the blog post url is formed this way:
pageTitle/blogTitle/publicationDate/blogPostTitle
There is no way a blog to be created with empty url. The reason for forming the url this way is that blog posts are organized in blogs and blog post's title can't be duplicated in a blog.
Hi Milena
For 6) the code I'm using is the same as in your post above, there is a content_id, but the id is NULL.
I also have a version using the second, bold bit of your code before the SaveChanges() of the first block.
8) is okay now, i think i'm understanding it a little clearer. I'm assuming a use case would be for multiple, aggregated blogs on the same base page.
9) is solved when setting the publication date as in your last post. There must be some caching going on somewhere that stores the old URLs for a while so that it doesn't immediately reflect any changes made in the database.
Thanks for your all help
Ryan
Hello Ryan,
Sorry for delayed response!
About 6) after CreateNew() method of the blogs facade, the blog is assigned with id which happens at provider level and more specifically at DataProviderBase:
public virtual object CreateItem(Type itemType)
return this.CreateItem(itemType, Guid.NewGuid());
See attached screenshot (contains debug information) that there is id of the blog after CreateNew() call. This gets persisted in the database upon SaveChanges() call though.
Let me know if you still experience any issues! Thanks.