Background: I have developed a couple of SharePoint 2010 web parts using Visual Studio with custom properties. I have also been able to set these properties with code so that they persist. I also know that SharePoint users can personalize pages & web parts with their own property values etc.
Question: Is it possible to set up a property that is always only user-specific?
In other words, I want my web part to have a property that starts out blank and is assigned a value once a user performs an action within the web part, but that value is specific to that user. Other users viewing the page would need to perform that action to get their own value. I would like for this to happen without the users having to manually personalize the page.
I tried setting attributes:
Personalizable( PersonalizationScope.User )
and later added
WebPartStorage( Storage.Personal )
and did some testing, but the value set by the first user always persisted to the second.
We haven't done a lot with personalization, so I'm really just curious if this is even possible.
You should be able to add an attribute [Personalizable(true)].
The WebPartStorage property (with Storage.Shared and Storage.Personal) is a holdover from the older API for SharePoint 2003, as documented here.
Take a look at Microsoft's walkthrough article here: Creating a Custom Web Part Editor in SharePoint 2010
The Web Part declares a private variable _tabList as a collection of TabData objects. This collection is wrapped through the TabList property. The property returns an empty collection if _tabList is null. The property is also marked Personalizable, to enable users to customize the tabs.
[...]
// Collection of tabs.
private List<TabData> _tabList;
// Property to hold the collection of tabs.
// Set the Personalizable attribute to true,
// to allow for personalization of tabs by users.
[Personalizable(true)]
public List<TabData> TabList {
get
{
if (this._tabList == null)
{
// Return an empty collection if null.
this._tabList = new List<TabData>();
}
return this._tabList;
}
set
{
this._tabList = value;
}
}
For more reading, check out Microsoft's documentation here: About Web Part Page personal and shared views
Notably, consider the following excerpt:
When a Web Part page is in personal page view, you:
Can usually view and modify the Layout and Appearance common Web Part properties, but not the Advanced ones. In some cases, the developer of a Web Part may decide not to display some or all common properties or you may not have permission to see them.
Can view and modify custom Web Part properties. In some cases, the developer of a Web Part may decide not to display some or all custom properties or you may not have permission to see them.
Can view and modify, but not delete, shared Web Parts with appropriate permission. Once you modify a shared Web Part, however, it becomes a personalized Web Part.
Can view and modify, but not delete, personalized Web Parts. The modified property values of these Web Parts apply only to you.
Can view, modify, and delete private Web Parts. These Web Parts only apply to you, no other users can see them, even in shared view.
By default, only Layout and Appearance properties are customized to the current user when editing a page in personal view. Custom properties are shared unless you explicitly make them personalizable.
Related
I have tried finding an answer to this question practically everywhere I could imagine, including here on StackOverflow. Unfortunately to no avail. So here it is.
I'm working on an Outlook Add-in (with Outlook 2021), and have developed some code that creates some ItemProperties specifically for use with that add-in. Now, when those properties are created, I can see them when I go to View->Settings->Advanced View Settings->Columns, as illustrated in the screenshot.
Screenshot of User-defined fields in Outlook
In some cases, though, I want to completely delete the properties. And as I know how to do that manually, as pointed out in the figure, I can't find out how to do that programmatically via C#. I have gone that far as to remove the properties from each mail containing that kind of property, like this:
IEnumerable<MailItem> listOfAssignedEmails = itemsToProcess.Where(
t => t.ItemProperties[MailExpiration.ExpirationDatePropertyName] != null);
foreach (MailItem email in listOfAssignedEmails)
{
// Note: The Delete() operation is deprecated. A more up-to-date method must be found.
email.ItemProperties[MailExpiration.ExpirationDatePropertyName].Delete();
email.Save();
}
... and yes, I know that the Delete() operation is deprecated; however, I couldn't find another method for removing the ItemProperty from the email (any suggestions are welcome).
Basically, the deletion of this Property is only going to be done very rarely (t. ex. if the user chooses to uninstall the Add-in. However, if there's any way to remove that property automatically, I would be happy to know.
Any suggestions will be greatly appreciated.
It is really a bad idea to remove a custom property from all emails that already have it: there is really no point since the user will never see them, but you will have to retouch (and thus change the last modified date) of a large number of emails.
Also note that named properties in MAPI are a finite resource - you can have at most 64k of them in a mailbox. Once a particular property mapping is used, you can never unmap it, even if there are no items that use that property.
Thirdly, doing anything Outlook related from an installer (rather than a VSTO addin) is a really bad idea - Windows installer runs in a service.
If you want to make sure the user no longer sees your custom fields as available properties in a view, you need to deal with the folder fields - they ar stored in a blob in a hidden (associated) message in that folder. OOM does not expose folder fields at all (if you don't count the AddToFolderFields parameter when calling UserProperties.Add). If using Redemption is an option (I am its author), it exposed RDOFolderFields object (accessible from RDOFolder2.FolderFields property) that allows to add or delete folder fields.
The list of properties shown on the screenshot belongs to the Folder.UserDefinedProperties property which returns a UserDefinedProperties object that represents the user-defined custom properties for the Folder object.
Use the ItemProperties.Remove method removes an object from the collection (from an item).
Use the ItemProperties property to return the ItemProperties collection. Use ItemProperties.Item(index), where index is the name of the object or the numeric position of the item within the collection, to return a single ItemProperty object.
I am currently reviewing an existing Sitecore project. One of the items has a controller rendering that outputs a form onto the Layout. In the Action Method, for the controller rendering, there is a line that seems to get the Item's Database Name credentials. I have had a look at the Item's Layout, however I can't find any Database field. I know that Sitecore.Context.Item is meant to get the current Item. However, I am know sure in the code below, how where Sitecore.Context.Item.Database.Name is pointing to. Any explanation would be really appreciated.
public ActionResult Form()
{
Item currentItem = Sitecore.Context.Item;
if (!IsValid(currentItem))
{
return Redirect(Sitecore.Context.Site.VirtualFolder);
}
FormModel model = new FormModel(currentItem);
model.PageModel.Db = Sitecore.Context.Item.Database.Name;
model.PageModel.ItemId = Sitecore.Context.Item.ID.ToString();
return View(model);
}
Sitecore.Context.Item.Database.Name provides the context database in which you are viewing the item. So if you are inside Experience Editor you will get master or if you are on site itself then you will get web.
Sitecore.Context is to provide context information like item, database or language. So for example Sitecore.Context.Item.Language will provide context language in which you are viewing the content on site.
Your "Database" property is not something you will find in a field or anything - it refers to the Sitecore database where the item is located. In a simple setup that will most likely be "master" or "web". The name property of the database will just refer to a string that indicates the database (master - web - ...).
As in Sitecore your item can come from different databases, this property can be used to identify that source. Published items will in a standard setup be in the web database, the master database will contain all items and versions and is used while editing.
Working with Sharepoint 2010, I have a class that inherits SPPersistedObject with various settings:
[Serializable] class Settings : SPPersistedObject
{
[Persisted] private string setting1; // getters and setters etc. exist for each field
...
}
These settings (properties) are supposed to be globally accessible from the application code. Every time the value of one or more of them changes, Update() method is called so that other parts of the code (i.e. other aspx pages) may read the correct, up-to-date value.
This works fine as long as I'm only accessing properties within the same application that updated them, e.g.: http://abc:5100/.../test.aspx updates Settings.Setting1, calls Update(); and other :5100 pages will now see the new value in their code.
However - and this is my problem - when I read Settings.Setting1 property from, say, http://abc:26233 /.../temp.aspx, the old value (pre-Update) is retrieved instead of the new one. This leads me to believe that the property is read from some kind of in-memory copy instead of from the updated store. The new value is retrieved only if I manually use 'iisreset /restart' beforehand, but that is not desirable.
I would greatly appreciate it if someone has any idea on how to update/read the properties so that the change is reflected across the entire farm, i.e. the value is read from a common permanent store.
Solution found by w128:
The solution is to use the SPPersistedObject.Clone() method on your class, e.g. (not the actual code, but illustrates the point):
Settings s = (Settings)SettingsObj.Clone();
return s.Setting1; // returns updated value
I'm building a DAL for a widget-based reporting application, its been designed in such a way that users pick, configure and deploy reporting 'widgets' to their home screens. Widgets can report across various kinds of company data - sites, brands, employees and so on.
Whilst all users can access all the widgets/reports, all users are not authorised to access all data. If I work for Company-A I can't view sales reports for Company-B or staff attendance data for a salesman at Company-C, however I can configure such a report and add it to my 'dashboard'.
At runtime, an intermediate 'DataService' class has the job of checking the user's credentials and, if access is permitted, returning the appropriate object collection to the client.
On the initial build I just returned an empty List if access to the data was not allowed, but this is also what I do if no data is returned by the report (which can happen). I'd like to show an 'Access Denied' message on the front end if the user isn't authorised to view the data but obviously if all I get back in either eventuality is an empty collection its impossible to know if this was because of insufficients rights or just no data.
I'd be grateful if you could suggest a way of coding around this, my first thought was to move the credential-checking into another object which in turn calls the data access class but time constraints mean this isn't an option.
The only thing I can think of, which goes against everything I've ever learnt, is to throw a custom exception e.g. InsufficientApplicationPrivilegeException if access isn't granted, but this smells bad.
Thanks for reading.
I think you have a couple of options. One is to make a composite object that your data service class returns. The composite object looks something like this: -
class DataResult<T>
{
IEnumerable<T> Data;
Result ServiceResult;
}
ServiceResult contains metadata about the outcome of your service call - it could be an enum which contains e.g. Success, AuthenticationFailure etc. etc.. You can then switch on this in order to do different behaviour.
An alternative option might be to use the NullObject pattern that shows a single item of data in the view which instead of real data simply shows "Access Denied" for the display properties of the object. The advantage of this approach is that your front-end doesn't need to have any conditional logic etc.; however if you want to show a specific message box or similar rather than just displaying a dummy row of data in your widget, then this probably isn't appropriate.
I have a "settings file" in my Winforms application called Settings.settings with a partial class for custom methods, etc. Is there a way to load / save dynamic settings based on arbitrary keys?
For example, I have some ListViews in my application in which I want to save / load the column widths; Instead of creating a width setting for each column for each list view I would like a simple method to load / save the widths automatically.
Below is an example of the save method I have tried:
internal sealed partial class Settings
{
public void SetListViewColumnWidths(ListView listView)
{
String baseKey = listView.Name;
foreach (ColumnHeader h in listView.Columns)
{
String key = String.Format("{0}-{1}", baseKey, h.Index);
this[key] = h.Width;
}
}
}
When running that code I get the error "The settings property 'TestsListView-0' was not found." Is there something I am missing?
Store your column width settings in an Xml Serializable object. Ie, something that implements IXmlSerializable then create a single setting entry of that type in Settings.settings.
A good option would probably be an Xml Serializable Dictionary. A quick google search found quite a few different blog posts that describe how to implement that.
As mentioned in other answers you'll need to ensure that this object is a User setting. You may also need to initialize the setting instance. Ie, create a XmlSerializableDictionary() instance and assign it to the setting if the setting is null. The settings subsystem doesn't create default instances of complex setting objects.
Also, if you want these settings to persist between assembly versions (ie, be upgradable) you will need to upgrade the settings on application startup. This is described in detail on Miha Markič's blog and Raghavendra Prabhu's blog.
I think the error
The settings property
'key' was not found.
occurs because the 'key' value does not exist in your settings file (fairly self-explanatory).
As far as I am aware, you can't add settings values programmatically, you might need to investigate adding all of the settings you need to the file after all, although once they are there, I think you'll be able to use the sort of code you've given to save changes.
To Save changes, you'll need to make sure they are 'User' settings, not 'Application'.
The Settings file is quite simple XML, so you might be able to attack the problem by writing the XML directly to the file, but I've never done it, so can't be sure it would work, or necessarily recommend that approach.
http://msdn.microsoft.com/en-us/library/cftf714c.aspx is the MSDN link to start with.
You can do Settings.Save() or similar on user settings, but note that such settings would NOT get persisted to the xxx.exe.config file in your app directory as you'd expect. They actually go somewhere deep inside the user folder (search your drive for xxx.exe.config to find it). Next time that you manually change xxx.exe.config in your app directory, the change will mysteriously not apply (the system is still using the saved one from the user directory).