This is the way I save a property of my webpart in a farm solution:
SPContext.Current.Web.AllowUnsafeUpdates = true;
SPFile file = SPContext.Current.File;
SPLimitedWebPartManager mgr = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
for (int index = 0; index < mgr.WebParts.Count; index++)
{
if (mgr.WebParts[index].ID == this.ID)
{
((MyWebpartType) mgr.WebParts[index]).MyStringProperty = "Hello World!";
mgr.SaveChanges(mgr.WebParts[index]);
}
}
SPContext.Current.Web.AllowUnsafeUpdates = false;
Works fine.
Now I have to achieve the same but in a sandbox solution but there is no SPLimitedWebPartManager available.
So how can I change a webpart property by code inside a sandbox solution webpart?
Found the solution, a call to SetPersonalizationDirty() in the webpart saves all properties.
You may define your own custom webpart property as below:
public partial class BannerSlider : System.Web.UI.WebControls.WebParts.WebPart
{
[WebBrowsable(true),
Personalizable(PersonalizationScope.Shared),
WebDescription("My WebPart Property"),
Category("Custom Properties"),
WebDisplayName("My WebPart Property")]
public string MyProperty
{
get { return __myProperty; }
set { _myProperty= value; }
}
protected void Page_Load(object sender, EventArgs e)
{
this.MyProperty = "Hello World";
Related
I want to track if the users have read sharepoint 2010 document center's documents, currently user infos are not stored in audit logs, is there any way to do it?
It gets stored in audit logs.
Enable auditing for that particular document library and then get the details using the following code:
SPSite oSPsite = new SPSite
SPList doclib= oSPWeb.Lists["doclib"];
SPWeb oSPWeb = oSPsite.OpenWeb()
SPListItemCollection doclibitems= doclib.Items;
foreach (SPListItem odoclibItem in doclibitems)
{
odoclibItem .Audit.AuditFlags = SPAuditMaskType.View;
// odoclibItem .Audit.AuditFlags = SPAuditMaskType
SPAuditQuery oquery = new SPAuditQuery(oSPsite);
oquery.RestrictToListItem(odoclibItem );
odoclibItem .Audit.Update();
SPAuditEntryCollection oAuditEntryCollection =SPsite.Audit.GetEntries(oquery);
foreach (SPAuditEntry entry in oAuditEntryCollection)
{
if (entry.Event == SPAuditEventType.View)
{
id = Convert.ToString(entry.UserId);
// get the user name and other details here
}
}
}
I found the solution. Here is the steps.
1- Create a class library.
2- Right click the library and add new item.
3- Select ASP.NET Module under Web node.
4- Add PreRequestHandlerExecute event handler inside Init.Here is my code.
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += context_PreRequestHandlerExecute;
}
5- Methods
void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
var app = sender as HttpApplication;
if (app != null)
{
string requesturl = app.Request.Url.ToString();
string file = string.Empty;
// if document opens in browser
if (app.Request.QueryString["Source"] != null && app.Request.QueryString["id"] != null && app.Request.QueryString["Source"].StartsWith(documentSiteUrl))
{
file = app.Request.QueryString["id"].Remove(0, app.Request.QueryString["id"].LastIndexOf('/'));
Worker(file);
}
// if document opened installed office apps or downloaded the document
if (requesturl.StartsWith(SiteUrl))
{
requesturl = requesturl.Remove(0, requesturl.LastIndexOf('/') + 1);
Worker(requesturl);
}
}
});
}
private void Worker(string requesturl)
{
#region ext control
List<string> extList = new List<string>(Exts.Split(';'));
bool result = false;
foreach (string item in extList)
{
if (requesturl.EndsWith(item))
{
result = true;
break;
}
}
#endregion
if ((!requesturl.Contains(".aspx")) && (!requesturl.EndsWith("/")) && result)
{
SPWeb web = SPContext.Current.Web;
String fileName = requesturl.Substring(requesturl.LastIndexOf("/") + 1);
// Add log
web.AllowUnsafeUpdates = true;
AddReadInfo(web.CurrentUser, fileName, web);
web.AllowUnsafeUpdates = false;
}
}
private void AddReadInfo(SPUser sPUser, string fileName, SPWeb web)
{
#region Logging
SPList logList = web.Lists.TryGetList("LogList");
if (logList != null)
{
SPListItem item = logList.Items.Add();
item["User"] = sPUser.Name;
item["Document"] = fileName;
item["Read"] = "Read";
item.Update();
}
#endregion
}
6- Don't forget signing the project.
7- Build Project.
8- Add dll to GAC and BIN folder under the C:\inetpub\wwwroot\wss\VirtualDirectories\80\ folder.
9- Open IIS Manager.
10- Find your site nod and select.
11- Open Modules.
12- Right click under modules page and select Add Managed Module option.
13- Give a name and select your module under dropdownlist.
14- IIS reset.
I've created a custom event document that extends the fields of the normal event document. I've added a field that can keep 0 to many category Ids in a pipe delimited list. Categories are stored in a custom table.
Here is my filter code:
public partial class CMSGlobalFiles_EventCategoryFilter : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnInit(EventArgs e)
{
SetupControl();
base.OnInit(e);
}
protected override void OnPreRender(EventArgs e)
{
if (RequestHelper.IsPostBack())
{
setFilter();
}
base.OnPreRender(e);
}
private void SetupControl()
{
if (this.StopProcessing)
{
this.Visible = false;
}
else if (!RequestHelper.IsPostBack())
{
InitializeCategory();
}
}
private void InitializeCategory()
{
CustomTableItemProvider customTableProvider = ne CustomTableItemProvider(CMSContext.CurrentUser);
string where = "";
string tableName = "customtable.EventCategory";
DataClassInfo customTable = DataClassInfoProvider.GetDataClass(tableName);
if (customTable != null)
{
DataSet dataSet = customTableProvider.GetItems(tableName, where, null);
if (!DataHelper.DataSourceIsEmpty(dataSet))
{
this.drpCategory.DataSource = dataSet;
this.drpCategory.DataTextField = "CategoryName";
this.drpCategory.DataValueField = "ItemGUID";
this.drpCategory.DataBind();
this.drpCategory.Items.Insert(0, new ListItem("(all)", "##ALL##"));
}
}
}
private void setFilter()
{
string where = null;
if (this.drpCategory.SelectedValue != null)
{
Guid itemGUID = ValidationHelper.GetGuid(this.drpCategory.SelectedValue, Guid.Empty );
if (itemGUID != Guid.Empty)
{
where = "EventCategory LIKE \'%" + itemGUID.ToString() + "%\'";
}
}
if (where != null)
{
this.WhereCondition = where;
}
this.RaiseOnFilterChanged();
}
}
This filter works great using a basic repeater and a document data source. When I use the event calendar it does not. I'm using Kentico version 6.0.30
The problem is in the different lifecycle of the EventCalendar, based on the CMSCalendar control which is based on standard .Net Calendar.
First of all, our developers discovered a way to fix this and allow your scenario to run by default. This fix will be included in the 6.0.33 hotfix (scheduled to go out on Friday 25th).
I'm sorry for this inconvenience.
Aside from this upcoming fix, it's also possible to make the EventCalendar to filter its results by modifying (cloning) the web part, integrating the filter controls directly into that web part and set the calendar's Where condition in the OnPreRender before the DataBind as
protected override void OnPreRender(EventArgs e)
{
calItems.WhereCondition = "some filtering condition";
...
If you can hotfix your CMS instance, it would be certainly less effort.
Regards,
Zdenek / Kentico Support
Using the following block of code, the listItem.Update fails with a NullReferenceException:
SPWeb web = null;
SPList list = null;
SPListItem listItem = null;
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(this.SiteUrl))
{
web = site.OpenWeb();
list = web.Lists[this.ListName];
listItem = list.Items.Add();
listItem["Background"] = "foo";
}
}
);
listItem.Update();
}
catch
{
}
finally
{
web.Dispose();
}
If I move the listItem.Update() method inside of the anonymous delegate, I get "Operation is not valid due to the current state of the object."
Yes, I've combed through SO and have tried many permutations without success.
Any ideas?
Update:
After the first comment, I tried to remove the anonymous delegate from the code to see if it fared any better:
// store the selected item to pass between methods
public T SelectedItem { get; set; }
// set the selected item and call the delegate method
public virtual void Save(T item)
{
SelectedItem = item;
try
{
SPSecurity.RunWithElevatedPrivileges(SaveSelectedItem);
}
catch
{
}
}
public virtual void SaveSelectedItem()
{
if (SelectedItem != null)
{
using (SPSite site = new SPSite(this.SiteUrl))
{
using(SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[this.ListName];
SPListItem listItem = list.Items.Add();
//UpdateListItem(listItem, SelectedItem);
listItem["Background"] = "foo";
listItem.Update();
}
}
}
}
And this still fails "Operation is not valid due to the current state of the object." In both code samples, it looks like site.Impersonating is false. I am using Windows Auth, and Impersonation in the web.config. This is running from the ASP.Net Development server.
I found an example from this site (blackninjasoftware). I create a reference to the site, grab its SystemAccount token and then create another reference to the site, using the admin token. It seemed a little hackish to me at first - but hey - I have a deadline.
Final working method body now looks like:
SPListItem new_item = null;
SPSite initialSite = new SPSite(this.SiteUrl);
using (var site = new SPSite(this.SiteUrl, initialSite.SystemAccount.UserToken))
{
// This code runs under the security context of the SHAREPOINT\system
// for all objects accessed through the "site" reference. Note that it's a
// different reference than SPContext.Current.Site.
using (var elevatedWeb = site.OpenWeb())
{
elevatedWeb.AllowUnsafeUpdates = true;
SPList list = elevatedWeb.Lists[this.ListName];
new_item = list.Items.Add();
UpdateListItem(new_item, item);
if (new_item != null)
{
new_item.Update();
}
}
}
initialSite.Dispose();
Can anyone see why this should not work:
SPSite topNavigationSite = new SPSite("http://moss");
SPWeb topNavigationWeb = topNavigationSite.OpenWeb();
SPNavigationNodeCollection topNavigationBarNodes = topNavigationWeb.Navigation.TopNavigationBar;
SPNavigationNode updateNode = topNavigationBarNodes.Navigation.GetNodeByUrl("/about");
updateNode.Url = "";
topNavigationWeb.Update();
I can see debugging that the url get's set to "" but when the page renders, the navigation still shows the url as /about/default.aspx
I'm running this in page_load and expected it to update the moss database with the new url value.
I know this is 3years old! but as there is no where online about updating a current url!
I had to do some debugging and this is what iv come up with! By the way I got the hint from
topNavigationWeb.Update();
indicating that its updating a list! hint hint!
a bit of background! I wanted to update the quick links from a list when they add, update and delete an item from the list! On my list I have two columns Title and URL!
I then created a project in VS 2010, its an event receiver that is connected only to that list (done in the elements.xml file)
within the .cs file I added item added, item deleting(not deleted ;) ) and item updated
public override void ItemAdded(SPItemEventProperties properties)
public override void ItemUpdated(SPItemEventProperties properties)
public override void ItemDeleting(SPItemEventProperties properties)
now within each method you can simply call this method!
add/update
public static void AddQuickLaunchItem(string header, string url, SPWeb web)
{
SPNavigationNodeCollection quickLaunch = web.Navigation.QuickLaunch;
// try to get quick launch header
SPNavigationNode nodeHeader =
quickLaunch.Cast<SPNavigationNode>().Where(n => n.Title == header).FirstOrDefault();
//if header not found create it
if (nodeHeader == null)
{
nodeHeader = quickLaunch.AddAsFirst(new SPNavigationNode(header, url,true));
}
else
{
web.AllowUnsafeUpdates = true;
nodeHeader = quickLaunch.Cast<SPNavigationNode>().Where(n => n.Title == header).First() ;
nodeHeader.Url = url;
web.AllowUnsafeUpdates = false;
}
nodeHeader.Update();
web.Update();
}
the first part is checking if the node exists using the title (header)! I'm comparing between what headers there are and from the list item:
SPNavigationNode nodeHeader =
quickLaunch.Cast<SPNavigationNode>().Where(n => n.Title == header).FirstOrDefault();
this part is the comparison:
n.Title == header
I'm getting these values (header and url / spweb) like so:
public static void AddQuickLaunchItem(string header, string url, SPWeb web)
the method that is calling the above would look like this:
private void addloopweblinks(SPSite siteCollection, SPItemEventProperties properties)
{
// Enumerate through each site and apply branding.
foreach (SPWeb web in siteCollection.AllWebs)
{
AddQuickLaunchItem(properties.ListItem["Title"].ToString(), properties.ListItem["URL"].ToString(), web);
}
}
and the above method is called within the itemadded and itemupdated ;) passing the values like so:
public override void ItemAdded(SPItemEventProperties properties)
{
using (SPSite siteCollection = new SPSite(properties.WebUrl))
{
if (siteCollection != null)
{
addloopweblinks(siteCollection, properties);
}
}
}
similar things can be done for the delete ;)
SPNavigationNode nodeHeader =
quickLaunch.Cast().Where(n => n.Title == header).FirstOrDefault();
that will get the node!
nodeHeader.delete();
nodeHeader.update();
that will delete the item!
so in your case what you need, I don't know if you noticed it was this part:
web.AllowUnsafeUpdates = true;
nodeHeader = quickLaunch.Cast<SPNavigationNode>().Where(n => n.Title == header).First() ;
nodeHeader.Url = url;
web.AllowUnsafeUpdates = false;
nodeHeader.Update();
web.Update();
as you can see what i mean by the hint! nodeHeader.Update(); didnt update the url, when debugging it does change but when i go to the update part it doesnt work :( and it clicked in i need web.AllowUnsafeUpdates = true; loool only becuse I experienced a similar situation before!
for you it would be:
using(SPSite topNavigationSite = new SPSite("http://moss"))
{
using(SPWeb topNavigationWeb = topNavigationSite.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPNavigationNodeCollection topNavigationBarNodes =topNavigationWeb.Navigation.TopNavigationBar;
SPNavigationNode updateNode = topNavigationBarNodes.Navigation.GetNodeByUrl("/about");
updateNode.Url = "";
updateNode.Update();
web.Update();
web.AllowUnsafeUpdates = false;
}
}
If the user doesnt have the sufficent rights than you need to encapsulate the above with runwithelevatedprivalages :) hope this helps :)
Have you tried:
updateNode.Update();
topNavigationWeb.Update();
It doesn't look like you are updating the SPNavigationNode object. (Note: you may not need the second Update call.)
As the question says - is there a way to determine which pages are including my web part?
If you're looking for code, I've got something for you. If you'd like to find all Content Query web parts then you would call my code like this:
FindWebPart("http://server.com/", "Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart");
Here's the code:
public static void FindWebPart(string siteCollectionUrl, string webPartName)
{
using (SPSite siteCollection = new SPSite(siteCollectionUrl))
{
using (SPWeb rootSite = siteCollection.OpenWeb())
{
FindWebPartHelper(rootSite, webPartName);
}
}
}
public static void FindWebPartHelper(SPWeb site, string webPartName)
{
// Search for web part in Pages document library
SPList pagesList = null;
try
{
pagesList = site.Lists["Pages"];
}
catch (ArgumentException)
{
// List not found
}
if (null != pagesList)
{
SPListItemCollection pages = pagesList.Items;
foreach (SPListItem page in pages)
{
SPFile file = page.File;
using (SPLimitedWebPartManager mgr = file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
try
{
SPLimitedWebPartCollection webparts = mgr.WebParts;
foreach (System.Web.UI.WebControls.WebParts.WebPart wp in webparts)
{
// Here perform the webpart check
// For instance you could identify the web part by
// its class name
if (webPartName == wp.GetType().ToString())
{
// Found a match! Now do something...
Console.WriteLine("Found web part!");
}
}
}
finally
{
// Needs to be disposed
mgr.Web.Dispose();
}
}
}
}
// Check sub sites
SPWebCollection subSites = site.Webs;
foreach (SPWeb subSite in subSites)
{
try
{
FindWebPartHelper(subSite, webPartName);
}
finally
{
// Don't forget to dispose!
subSite.Dispose();
}
}
}
Ofcourse you can make little changes to this code. Currently it does a string comparison, but it's easy to do it in a more typed way. Have fun!
By way of an alternative, if you want to test web part pages including the default page on collaboration sites, you can use the following code snippet which uses the Files property of the SPWeb object:
private static void FindWebPart(string siteUrl, string webPartName)
{
using (var site = new SPSite(siteUrl))
{
foreach (SPWeb web in site.AllWebs)
{
foreach (var file in web.Files.Cast<SPFile>().Where(file => file.Name.EndsWith("aspx")))
{
FindWebPartOnPage(webPartName, file);
}
var pagesTemplateType = (SPListTemplateType)Enum.Parse(typeof(SPListTemplateType), "850");
foreach (var documentLibrary in web.Lists.Cast<SPList>().Where(list => list.BaseTemplate == pagesTemplateType || (list.BaseTemplate == SPListTemplateType.DocumentLibrary && list.Title.Contains("Pages"))))
{
foreach (var file in documentLibrary.Items.Cast<SPListItem>().Where(item => item.File.Name.EndsWith("aspx")).Select(item => item.File))
{
FindWebPartOnPage(webPartName, file);
}
}
web.Dispose();
}
}
}
private static void FindWebPartOnPage(string webPartName, SPFile file)
{
using (var webPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
if (webPartManager.WebParts.Cast<WebPart>().Any(webPart => webPart.GetType().Name == webPartName))
{
Console.WriteLine(file.ServerRelativeUrl);
}
webPartManager.Web.Dispose();
}
}
Note: the Pages library created by the Publishing feature does not have a BaseTemplate value of SPListTemplateType.DocumentLibrary; instead it is represented by a "hidden" value of 850.
This is called a similar manner to LeonZandman's answer, however only the class name is used to supply a match:
FindWebPart("http://yoursite.com/", "MyWebPart");