Tuesday, June 24, 2008
#
General List management:
- SharePoint lists are basically just Database Tables (or excel worksheets). You have control over how many columns exist, and what their types are.
- SharePoint columns are generally set to these core types: 1 line of text, multiple lines of text (w/ or wo/ rich formatting), numerical, dates, choices, user/group assignment, lookup
- SharePoint choice columns are flat choice lists, but a hierarchy can be simulated by having choices such as: Food, Food:Groceries, Food:Restaurant, Food:CoffeeShop. That would allow you to choose "Food" as the parent node OR "Food:CoffeeShop" if you wanted to choose the child node. Maintaining the values in a choice column requires hard-core list maintenance.
- SharePoint choice columns may allow "Fill-in" choices if desired where the user is given a chance to choose a value from the drop down selector, OR type in their own value. The fill-in choice is NOT automatically added to the choice list for general use (the user would have to type the exact same value each time they wanted to use it).
- SharePoint list attachments and folders can be enabled or disabled on any list. List attachments let you attach files to individual list items.
- SharePoint list views can present vastly different presentations based on the same underlying items stored in the list. One view is the default for the list, others may be created as necessary. All views must be configured with: what columns to display; what order to display the columns; how to sort/group/filter the items; other presentation and content choices. This is a huge benefit as it typically allow designs utilizing one underlying list for many purposes rather than requiring the difficult management of many seperate lists.
- SharePoint list web parts are used to put lists on a web part page. Each list web part could present a different view of the same underlying list. This is a huge benefit for presenting the user with many different views and rollups of one list on a single page.
Advanced List management:
- SharePoint lookup columns are like choice columns, but refer to another SharePoint list for values instead. Maintaining the values in a lookup column merely requires adding an entry to the referenced list. The referenced list could be ANY type of list: Custom List, Tasks, Contacts, Calendar, Document Library, …
- SharePoint Site Columns and Content Types allow list maintainers to define a set of fields that need to be entered and managed together in a list that supports several Content Types. For example, we could define 5 columns for a "design tool" Content Type and 8 columns for a "runtime tool" Content Type. When someone adds/edits/views a "design tool" item, the system will display the 5 columns defined for it. When manipulating a "runtime tool" item, it will use the associated 8 columns. This approach works best when there are overlapping sets of column definitions, otherwise if there were no commonality, they might as well just be separate lists.
- SharePoint list permissions can be set to allow/restrict access to the list as a whole.
- SharePoint list item permissions can be set to allow granular permissions per item. This must be maintained manually per item (high burdon).
- SharePoint list item permissions can be set to allow: Read access to everyone, or just a user's own items; Edit access to all, none, or just a user's own items.
- SharePoint list versioning and approval handling can be enabled or disabled on any list.
- SharePoint workflows are possible, but are pretty advanced.
Thursday, April 03, 2008
#
When to call Dispose() on your SharePoint WSSv3 site and web objects seems like an ornery topic. Dispose should be releasing memory and resources, and not actually writing or destroying any data, so not calling it properly could cause leaks of memory or other resources. Here is the general rule I've learned.
1) If you instantiate the object with “new” or OpenWeb, you need to Dispose() of it.
2) If you indirectly instantiate the web object by using SPSite.RootWeb, then you need to Dispose() of it... but not in the way you might think.
This is what SPSite.RootWeb is doing:
public SPWeb RootWeb
{
get
{
if (this.m_rootWeb == null)
{
this.m_rootWeb = this.OpenWeb(this.ServerRelativeUrl);
this.m_rootWebCreated = true;
}
return this.m_rootWeb;
}
}
So now it makes sense as to why SPSite.RootWeb might need special care to Dispose() just as if you had called OpenWeb... because you effectively did, since SPSite.Dispose() doesn't dispose the web object.
BUT, you could Dispose() of the SPWeb, OR the SPSite object to release that SPWeb. If you Dispose() the SPWeb, the SPSite gets a little confused, but not tragically (m_rootWeb ends up null, but m_rootWebCreated remains true). If you Dispose() of the SPSite instead, everything is properly Dispose()ed and all state remains consistent. With the varied advice on when and how to Dispose of the SPWeb from the SPSite.RootWeb, it is lucky that doing extra Dispose() calls don't do anything, or even error.
Look at this debugging session. Notice how the internal state of the “site“ object changes based on the worst case scenario of getting the RootWeb twice and Dispose()ing it between the references to RootWeb (I was trying to cause an error by calling Dispose() then getting the RootWeb again). The most interesting revelations are that Dispose()ing the Web immediately affects the state of its parent SPSite, and that Dispose()ing the SPSite automatically, and completely, cleans up the SPWeb gotten via RootWeb.
static void Main(string[] args)
{
SPSite site = new SPSite("http://localhost");
SPWeb web = null;
try
{
// site.m_rootWeb == null
// site.m_rootWebCreated == false
ShowDetails(site);
// site.m_rootWeb == null
// site.m_rootWebCreated == true
web = site.RootWeb;
}
finally
{
// site.m_rootWeb == web
// site.m_rootWebCreated == true
// web.m_closed = false
site.Dispose();
// site.m_rootWeb == null
// site.m_rootWebCreated == false
// web.m_closed = true
web.Dispose();
// site.m_rootWeb == null
// site.m_rootWebCreated == false
// web.m_closed = true
web.Dispose(); // Extra Disposes do nothing
// site.m_rootWeb == null
// site.m_rootWebCreated == false
// web.m_closed = true
}
}
static void ShowDetails(SPSite site)
{
SPWeb web = null;
try
{
// site.m_rootWeb == null
// site.m_rootWebCreated == false
web = site.RootWeb;
}
finally
{
// site.m_rootWeb == web
// site.m_rootWebCreated == true
// web.m_closed = false
web.Dispose(); // This is where SPSite's state first gets confused.
// site.m_rootWeb == null
// site.m_rootWebCreated == true
// web.m_closed = true
}
}
NOTE: The above example intentionally uses the long hand try/finally blocks to be clear about the point. The better way to code Dispose() is with the C# “using” directive to implicitly call Dispose(), such as “using (SPWeb web = site.RootWeb) { do something; }”
Wednesday, April 02, 2008
#
Though I'm rather biased, I think it would be very cool to have Web Routines in Microsoft Internet Explorer 8.
It would be a useful feature for the users, and useful vehicle for advancing Microsoft's on-line advertising aspirations.
http://aadhoc.blogspot.com/2008/03/web-routines.html
I have gotten some positive feedback from Microsoft employees.
It could work well in Google's web-based offerings too.
Monday, March 31, 2008
#
Can you guess what this Javascript code does and why? (I didn't even remove my comments)
I wrote this interesting bit of Javascript for my MOSS portal site out of necessity. If someone figures out why I'm using this, we could discuss the alternatives :-) Todd isn't allowed to guess because he knows !
function PageViewerWPVisibility(show) {
// Open the PageViewerWP if found
if (window != top) {
// If in edit mode, leave alone
var versionLink = window.parent.document.getElementById('pvi1_anchor');
if (versionLink != null) {
return;
}
// Find this iframe
// iframesrc = "/sites/Somesite/Pages/Somepage.aspx"
// windowsrc = "http://hostname/sites/Somesite/Pages/Somepage.aspx"
var iframes = window.parent.document.getElementsByTagName('iframe');
var windowsrc = window.location.href.toLowerCase();
var iframe = null;
for (var i=0; i<iframes.length; i++) {
var iframesrc = iframes[i].src.toLowerCase();
var pos = windowsrc.indexOf(iframesrc);
if (pos == (windowsrc.length - iframesrc.length)) {
iframe = iframes[i];
break;
}
}
if (iframe == null) { return; }
// Example: MSOPageViewerWebPart_WebPartWPQ13
var iframeid = iframe.parentNode.id;
var wpqNumPos = iframeid.indexOf("WebPartWPQ") + 10;
var wpqNum = parseInt(iframeid.substr(wpqNumPos));
var zonecell = window.parent.document.getElementById("MSOZoneCell_WebPartWPQ"+wpqNum);
if (show) {
// Show hidden cell and remove "(Hidden)" text.
zonecell.childNodes[0].style.display = "inline";
var titlenode = window.parent.document.getElementById("WebPartTitleWPQ"+wpqNum);
if (titlenode.innerHTML) {
titlenode.innerHTML = titlenode.innerHTML.replace(/[\s\u202C\u202D]*\(Hidden\)[\s\u202C\u202D]*/gm, "");
titlenode.title = titlenode.title.replace(/\s*\(Hidden\)\s*/g, "");
}
}
else {
// Ensure cell is hidden.
zonecell.style.display = "none";
zonecell.parentNode.style.display = "none";
}
}
}