Free SharePoint Organizational Chart WebPart SimpleChart for SharePoint

I have written all sorts of organizational chart WebParts in the past leveraging various rendering engines and complex ruling, outputting in various formats with assorted hooked behavior wired to the org chart. Having studied users while they are interacting with the org chart software, something that I found is glitzy, flashy eye-candy org chart features have about an interest shelf-life of 5 minutes for the typical user. While numerous business circumstances can cultivate all types of useful material to incorporate into the software, they rarely make a long-term practical contribution to the applicability of the org chart software, and in my opinion deteriorate the direct usefulness. As by definition an organization chart is a diagram that shows the structure of an organization and the relationships and relative ranks of its parts and positions/job keeping it strictly within this scope is vital for product success.

So I decided to sit down, and write an organizational chart WebPart that just makes an org chart. That’s it. It draws an org chart. It doesn’t do anything else. But it sure can draw an org chart. A really simple one that is consummately faster than contestant WebParts.

To realize this requirement, I decided to utilize the Google Visualization API since it supplied fundamental minimum functions to integrate obligatory, decisive features. Using a primitive SharePoint list as the visualization data source it is also very unpretentious, generic and not irrevocably bound to an automated source. This obviously has pros and cons, but for me having control over the display is a big pro.

Outside of the lean features provided by the Google Visualization API, I wanted something that used a rendering engine that I didn’t have to maintain or even one that another company maintained and pushed out components as a shared library. I have had problems with that in the past with organizations using analogous libraries, and they might move a little quicker than I in retrofitting, so products reference discrepancy can occur.

Lastly, I wanted something small….a really concise solution not composed of numerous moving parts (under 10k packaged). Just a very maintainable, low chance of exception approach that had zero gold plating while staying within the realm of what a functional org chart should do. I wanted it as lean as possible, focusing not on features but on rendering speed.

Onto the SimpleChart WebPart!

The WebPart description file is Feature controlled, so activate the SimpleChart For SharePoint Feature from the  Site collection features:

9-29-2009 3-36-09 PM

One the activated the WebPart is pretty easy to find from the gallery:

9-29-2009 3-38-05 PM

Here are the default WebPart properties. Some of the properties will assume default values when not provided (like color etc.), others are necessary for the WebPart display.

9-29-2009 3-12-42 PM

Display Properties

The org chart has nominal configurable properties to manipulate the WebPart display:

9-29-2009 3-16-54 PM

(1) Allow HTML If in the SharePoint list feed, the strings are stored as HTML tags they will be rendered as HTML. Best to keep this on.

(2) Allow Collapse If you want to be really fancy, this makes the boxes collapse up and down. Oh la la.

(3) Background Color – For each element in the org chart the Hex Triplet (HTML Color) provided will apply style to the *unselected* boxes.

(4) Selected Color – For each element in the org chart the Hex Triplet (HTML Color) provided will apply style to the *selected* boxes.

Operational Properties

The other properties are related directly to the WebPart operation. In order to support a generic list datasource, the WebPart expects you to provide the SharePoint list name and associated field names, correlating to the properties of the user. Don’t worry about CAML delimiters; the WebPart will parse all the values correctly internally. So, before you start using the WebPart, make a list with:

  • Employee Name
  • Superior ID
  • Employee Title
  • Employee Phone
  • Employee Address
  • Employee Email
  • Employee City
  • Employee State
  • Employee Country

9-29-2009 3-19-06 PM

So what’s it look like? Here is a screenshot of my list data source:

9-29-2009 3-21-24 PM

Now in the above, take note that the TITLE FIELD IS CHANGED TO EMPLOYEE TITLE. I used a custom list as the base template. If you really want you can download it here. I didn’t want to have a field that had no relevancy in the list, so it is best to repurpose it to hold the employee title.

The output from this data in SimpleChart is:

9-29-2009 3-32-31 PM

Pretty much all the fields being used are self -explantory. Superior (Manager) ID is the integer that correlates to to the Employee ID of the desired branch, that is pretty much the only relational field. Obviously, toggling the color values you can make some pretty awful charts:

9-29-2009 3-41-40 PM

leads to this beauty:

9-29-2009 3-43-00 PM

If you want support, use the contact form and drop me a line, leave something in the comments, whatever. I can tailor probably tailor the part to your organization requirements as well if you need help.

DOWNLOAD THE SIMPLECHART INSTALL PACKAGE

EDIT:

Download is down, new version out 10/16/2009 COB.

EDIT:

You can download the new version here.

I am considering extending the data sources to use either profiles or AD DS lookups as well. If there is sufficient interest I might just do that. Leave feature requests in the comments! I generally will put them if I I get bored.

Share

SimpleSecurity SharePoint WebPart for Easing SharePoint Security

One of the most advantageous features of SharePoint is the capacity to empower users, allowing greater generation of propositional knowledge, alleviating several IT support requirements, etc. However, empowering users with content and communication control unsurprisingly incorporates maintenance of security attributes associated with content and that contents storage instruments.

This came up at a client I was at, where they really, really wanted an extraordinarily unpretentious way to both show a exceedingly simple summary of existing site security, supplemented with only the most basic actions. So trying to keep the amount of controls being used in the WebPart under 6, we incorporated four fundamental event bound controls, and two dedicated to displaying information. So we could have context when discussing the product, we called it SimpleSecurity for reasons that don’t require explanation.

So, keeping the WebPart as nominal as possible, the below illustrates the process:

SimpleSecurity_Diagram

It’s pretty self-explanatory, but the gist of it is the WebPart sits somewhere on the site since it is going to use the current web context. When the user first adds the WebPrart to the page, if the required security groups don’t exist, they are created by the WebPart. Default grouping conventions being limited in SharePoint imply that abstracting this layer was desirable. Once existing, they are added into two list box controls that display the users available for actions, and the users that already have had actions applied to them. Using a simple movement process between those list boxes, the represented actions are executed.

I guess it’s kinda easier to show in screen shots, so here are some really simple examples:

In The Gallery

SimpleSecuritySharePointSafeControl

Adding the SimpleSecurity WebPart to the Page

SimpleSecurityWebPartPage

Auto-Creating the Required Groups

SimpleSecuritySharePointNotification

Groups Created, Start Doing Stuff

SimpleSecurityroupin

Adding a Reader, Contributor, and Administrator

SimpleSecuritySharePointAddLevels

Download the WSP here. All the images etc. are all bundled into the cabinet as well.

Happy Labor Day!

Share

PersistenceManager

The PersistenceManager class is a static helper class that manages the commonly used objects like the connection string, the ADO.NET connection, and the current transaction.

[csharp]
//*****************************************************************************
// This file is part of the data access layer example to the ASP.NET 2.0 provider database
// This file was written by Adam Buenz [WSS MVP] of ARB Security Solutions, LLC
// http://www.sharepointsecurity.com
//
// This file and its parts is free for re-distribution, for use in both free
// and commercial applications, however this header must remain intact for legal
// use. The data access layer example is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//*****************************************************************************
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Aspnet.Provider.Datalayer.Commands;
using Aspnet.Provider.Datalayer.DataTransferObjects;

namespace Aspnet.Provider.Datalayer
{
///
/// The AspnetApplications class.
///
public partial class AspnetApplications : IPersistable
{
#region Members

private bool isNew;
private bool _isNew;
private string applicationname;
private string loweredapplicationname;
private Guid applicationid;
private string description;

#endregion

#region Properties

///
/// The Applicationname.
///
[StringInfo(256, true)]
public virtual string Applicationname
{
get { return this.applicationname; }
set { this.applicationname = value; }
}

///
/// The Loweredapplicationname.
///
[StringInfo(256, true)]
public virtual string Loweredapplicationname
{
get { return this.loweredapplicationname; }
set { this.loweredapplicationname = value; }
}

///
/// The Applicationid.
///
public virtual Guid Applicationid
{
get { return this.applicationid; }
set { this.applicationid = value; }
}

///
/// The Description.
///
[StringInfo(256, false)]
public virtual string Description
{
get { return this.description; }
set { this.description = value; }
}

#endregion

#region ColumnNames

///
/// The corresponding schema name.
///
internal const string SchemaName = “dbo”;

///
/// The corresponding table name.
///
internal const string TableName = “aspnet_Applications”;

///
/// The column names.
///
internal class ColumnNames
{
///
/// The column name of the Applicationname property.
///
public const string Applicationname=”ApplicationName”;
///
/// The column name of the Loweredapplicationname property.
///
public const string Loweredapplicationname=”LoweredApplicationName”;
///
/// The column name of the Applicationid property.
///
public const string Applicationid=”ApplicationId”;
///
/// The column name of the Description property.
///
public const string Description=”Description”;
}

#endregion

///
/// The default constructor.
///
public AspnetApplications()
{
this.isNew = true;
PersistenceManager.InvokeInstanceMethod(this, “CreationComplete”, null);
}

///
/// The constructor of the required fields.
///
///
The Applicationname. ///
The Loweredapplicationname. ///
The Applicationid. public AspnetApplications(string applicationname, string loweredapplicationname, Guid applicationid)
{
this.applicationname = applicationname;
this.loweredapplicationname = loweredapplicationname;
this.applicationid = applicationid;

this.isNew = true;
PersistenceManager.InvokeInstanceMethod(this, “CreationComplete”, null);
}

///
/// The constructor from IDataReader.
///
///
An initalized IDataReader. internal AspnetApplications(IDataReader reader)
{
if ((reader[“ApplicationName”] != null) && (reader[“ApplicationName”] != DBNull.Value))
this.applicationname = (string)reader[“ApplicationName”];
if ((reader[“LoweredApplicationName”] != null) && (reader[“LoweredApplicationName”] != DBNull.Value))
this.loweredapplicationname = (string)reader[“LoweredApplicationName”];
if ((reader[“ApplicationId”] != null) && (reader[“ApplicationId”] != DBNull.Value))
this.applicationid = (Guid)reader[“ApplicationId”];
if ((reader[“Description”] != null) && (reader[“Description”] != DBNull.Value))
this.description = (string)reader[“Description”];

this.isNew = false;
}

///
/// Creates an IDbCommand to insert an object into the database.
///
/// An initialized IDbCommand object.
internal virtual IDbCommand CreateInsertCommand()
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = “insert into [dbo].[aspnet_Applications] ([ApplicationName], [LoweredApplicationName], [ApplicationId], [Description]) values (@applicationname, @loweredapplicationname, @applicationid, @description)”;
cmd.Parameters.AddWithValue(“@applicationname”, this.applicationname);
cmd.Parameters.AddWithValue(“@loweredapplicationname”, this.loweredapplicationname);
cmd.Parameters.AddWithValue(“@applicationid”, this.applicationid);
if (String.IsNullOrEmpty(this.description))
cmd.Parameters.AddWithValue(“@description”, DBNull.Value);
else
cmd.Parameters.AddWithValue(“@description”, this.description);

cmd.Connection = PersistenceManager.Connection;
return cmd;
}

///
/// Creates an IDbCommand to update an object in the database.
///
/// An initialized IDbCommand object.
internal virtual IDbCommand CreateUpdateCommand()
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = “update [dbo].[aspnet_Applications] set [ApplicationName]=@applicationname, [LoweredApplicationName]=@loweredapplicationname, [Description]=@description where ([ApplicationId]=@applicationid)”;
cmd.Parameters.AddWithValue(“@applicationname”, this.applicationname);
cmd.Parameters.AddWithValue(“@loweredapplicationname”, this.loweredapplicationname);
if (String.IsNullOrEmpty(this.description))
cmd.Parameters.AddWithValue(“@description”, DBNull.Value);
else
cmd.Parameters.AddWithValue(“@description”, this.description);
cmd.Parameters.AddWithValue(“@applicationid”, this.applicationid);

cmd.Connection = PersistenceManager.Connection;
return cmd;
}

///
/// Creates an IDbCommand to delete an object in the database.
///
/// An initialized IDbCommand object.
internal virtual IDbCommand CreateDeleteCommand()
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = “delete from [dbo].[aspnet_Applications] where ([ApplicationId]=@applicationid)”;
cmd.Parameters.AddWithValue(“@applicationid”, this.applicationid);

cmd.Connection = PersistenceManager.Connection;
return cmd;
}

///
/// Persists the object.
///
public virtual void Persist()
{
PersistenceManager.InvokeInstanceMethod(this, “PrePersist”, null);

IDbCommand cmd;

if (this.isNew)
cmd = this.CreateInsertCommand();
else
cmd = this.CreateUpdateCommand();

cmd.Transaction = PersistenceManager.Transaction;
PersistenceManager.RegisterInTransaction(this);

bool connWasClosed =
PersistenceManager.Connection.State.Equals(ConnectionState.Closed) ||
PersistenceManager.Connection.State.Equals(ConnectionState.Broken);

try
{

if (connWasClosed)
PersistenceManager.Connection.Open();

cmd.ExecuteNonQuery();

this.isNew = false;

if (connWasClosed)
PersistenceManager.Connection.Close();
}
catch (Exception ex)
{
if (connWasClosed)
PersistenceManager.Connection.Close();
throw ex;
}
}

///
/// Deletes the object.
///
public virtual void Delete()
{
PersistenceManager.InvokeInstanceMethod(this, “PreDelete”, null);

IDbCommand cmd = this.CreateDeleteCommand();
cmd.Transaction = PersistenceManager.Transaction;

bool connWasClosed =
PersistenceManager.Connection.State.Equals(ConnectionState.Closed) ||
PersistenceManager.Connection.State.Equals(ConnectionState.Broken);

try
{
if (connWasClosed)
PersistenceManager.Connection.Open();

cmd.ExecuteNonQuery();

if (connWasClosed)
PersistenceManager.Connection.Close();
}
catch (Exception ex)
{
if (connWasClosed)
PersistenceManager.Connection.Close();
throw ex;
}
}

///
/// Retrieves a AspnetApplications object by its primary key (Throws System.DataException).
///
///
The Applicationid. /// The AspnetApplications object.
public static AspnetApplications Get(Guid applicationid)
{
AspnetApplications aspnetapplications;
if (AspnetApplications.TryGet(applicationid, out aspnetapplications))
return aspnetapplications;
else
throw new DataException(“‘AspnetApplications’ object not found.”);
}

///
/// Tries to retrieve a AspnetApplications object by its primary key.
///
///
The Applicationid. ///
The found AspnetApplications or null if the primary key value does not exist. /// True if the AspnetApplications exists, else false.
public static bool TryGet(Guid applicationid, out AspnetApplications aspnetapplications)
{
SqlCommand cmd = new SqlCommand(“select * from [dbo].[aspnet_Applications] where ([ApplicationId]=@applicationid)”);
cmd.Parameters.AddWithValue(“@applicationid”, applicationid);

IList list = AspnetApplications.Query(cmd);

if (list.Count == 0)
{
aspnetapplications = null;
return false;
}
else
{
aspnetapplications = list[0];
return true;
}
}

///
/// For internal use only.
///
public virtual void SaveState()
{
this._isNew = this.isNew;
}

///
/// For internal use only.
///
public virtual void RestoreState()
{
this.isNew = this._isNew;
}

///
/// Refreshes the internal state of the object.
///
/// This method should be called after xml deserialization to refresh internal flags.
public virtual void RefreshState()
{
AspnetApplications aspnetapplications;

if (AspnetApplications.TryGet(this.applicationid, out aspnetapplications))
{
this.isNew = false;
}
else
{
this.isNew = true;
}
}

///
/// Returns a Data Transfer Object of this AspnetApplications.
///
/// A Data Transfer Object of this AspnetApplications.
public virtual AspnetApplicationsDTO GetDTO()
{
AspnetApplicationsDTO dto = new AspnetApplicationsDTO();

dto.Applicationname = this.Applicationname;
dto.Loweredapplicationname = this.Loweredapplicationname;
dto.Applicationid = this.Applicationid;
dto.Description = this.Description;

return dto;
}

///
/// Applies a Data Transfer Object data to this AspnetApplications.
///
///
The Data Transfer Object. public virtual void SetDTO(AspnetApplicationsDTO dto)
{
this.Applicationname = dto.Applicationname;
this.Loweredapplicationname = dto.Loweredapplicationname;
this.Applicationid = dto.Applicationid;
this.Description = dto.Description;
}

///
/// Perfoms a query on AspnetApplications objects.
///
///
An IDbCommand containing the select statement. /// A result list of AspnetApplications objects.
internal static IList Query(IDbCommand command)
{
command.Connection = PersistenceManager.Connection;
command.Transaction = PersistenceManager.Transaction;

bool connWasClosed =
PersistenceManager.Connection.State.Equals(ConnectionState.Closed) ||
PersistenceManager.Connection.State.Equals(ConnectionState.Broken);

try
{
if (connWasClosed)
PersistenceManager.Connection.Open();

List list = new List();
IDataReader reader = command.ExecuteReader();

using (reader)
{
while (reader.Read())
list.Add(new AspnetApplications(reader));
}

if (connWasClosed)
PersistenceManager.Connection.Close();

return list;
}
catch (Exception ex)
{
if (connWasClosed)
PersistenceManager.Connection.Close();
throw ex;
}
}

///
/// Finds all AspnetApplications objects with a certain Applicationname value.
///
///
The Applicationname value (‘*’ can be used as a wildcard). /// All AspnetApplications objects with a certain Applicationname value.
public static IList FindByApplicationname(string applicationname)
{
return AspnetApplications.Query(AspnetApplicationsCommands.FindByApplicationname(applicationname));
}

///
/// Finds all AspnetApplications objects with a certain Loweredapplicationname value.
///
///
The Loweredapplicationname value (‘*’ can be used as a wildcard). /// All AspnetApplications objects with a certain Loweredapplicationname value.
public static IList FindByLoweredapplicationname(string loweredapplicationname)
{
return AspnetApplications.Query(AspnetApplicationsCommands.FindByLoweredapplicationname(loweredapplicationname));
}

///
/// Finds all AspnetApplications objects with a certain Description value.
///
///
The Description value (‘*’ can be used as a wildcard). /// All AspnetApplications objects with a certain Description value.
public static IList FindByDescription(string description)
{
return AspnetApplications.Query(AspnetApplicationsCommands.FindByDescription(description));
}

}
}

[/csharp]

Share