Taylohtio/FusionCharts/SP2010/WebPartCode/ChartFromList.cs

632 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
namespace FusionCharts.WebParts
{
[Guid("3dd2d2e7-b5a3-4165-b3d1-421774fafa0f")]
public class ChartFromList : WebPart, ITrace
{
#region PrivateMembers
private bool _error = false;
private string _chartId = null; // null by default (important)
private ChartTypeRestricted _chartType;
private int _chartHeight = Utils.DefaultHeight; // default height
private int _chartWidth = Utils.DefaultWidth; // default width
private string _listName = ""; // The name of the list to use
private string _viewName = ""; // The name of the view to use
private string _chartTitle = ""; // The title of the chart
private string _xValue = ""; // The name of the column of the list to use as horizontal value
private string _xTitle = ""; // The title of hortizontal axis
private string _yValue = ""; // The name of the column of the list to use as vertical value
private string _yTitle = ""; // The title of vertical axis
private int _yDecimalPrecision = 0; // number of decimal places for y axis
private bool _yNumberScale = false; // y axis scaling option
private string _yNumberPrefix = ""; // optional prefix for y axis(e.g., "$")
private string _colors = null; // null by default (important). Colors that will be used
private bool _excelExport = false; // display/hide the custom button to export chart to excel
private bool _xmlExport = false; // display/hide the custom button to export chart to xml file
private GroupAction _action;
private string _myBuildVersion = "ChartFromList: 2013-02-13-1200";
private string _myTrace = "";
#endregion
#region CustomProperties
/// <summary>
/// The title of the chart
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Chart Title")]
[WebDescription("The title of the chart (caption).")]
public string ChartTitle
{
get { return _chartTitle; }
set { _chartTitle = value; }
}
/// <summary>
/// The type of chart to display
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Chart Type")]
[WebDescription("The type of chart that will be displayed")]
public ChartTypeRestricted ChartType
{
get { return _chartType; }
set { _chartType = value; }
}
/// <summary>
/// The height of the chart that will be displayed
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Chart Height")]
[WebDescription("The height of the chart to display")]
public int ChartHeight
{
get { return _chartHeight; }
set { _chartHeight = value; }
}
/// <summary>
/// The width of the chart that will be displayed
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Chart Width")]
[WebDescription("The width of the chart to display")]
public int ChartWidth
{
get { return _chartWidth; }
set { _chartWidth = value; }
}
/// <summary>
/// The list to use to chart
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("List Name (SharePoint List)")]
[WebDescription("The list to use to chart")]
public string ListName
{
get { return _listName; }
set { _listName = value; }
}
/// <summary>
/// The view of the list to use to chart
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("View Name (SharePoint View). If empty the default view will be used.")]
[WebDescription("The view (of the list) to use in order to generate a graph")]
public string ViewName
{
get { return _viewName; }
set { _viewName = value; }
}
/// <summary>
/// Name of the column to use as the horizontal value
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Name of the column to use as horizontal (x) value")]
[WebDescription("This should be a existing column name of a SharePoint List")]
public string XValue
{
get { return _xValue; }
set { _xValue = value; }
}
/// <summary>
/// Name of the column to use as the vertical value
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Name of the column to use as vertical (y) value. Leave empty if you select a 'Count' action below")]
[WebDescription("This should be a existing column name of a SharePoint List")]
public string YValue
{
get { return _yValue; }
set { _yValue = value; }
}
/// <summary>
/// Group action to perform
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Action to perform over columns")]
[WebDescription("")]
public GroupAction Action
{
get { return _action; }
set { _action = value; }
}
/// <summary>
/// Title of the horizontal values
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Horizontal (x) title to display")]
[WebDescription("Title of the horizontal values that will be displayed on the chart")]
public string XTitle
{
get { return _xTitle; }
set { _xTitle = value; }
}
/// <summary>
/// Title of the vertical values
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Vertical (y) title to display")]
[WebDescription("Title of the vertical values that will be displayed on the chart")]
public string YTitle
{
get { return _yTitle; }
set { _yTitle = value; }
}
/// <summary>
/// Number of decimal places for the vertical values
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Vertical (y) decimal precision")]
[WebDescription("Number of decimal places to show in vertical values")]
public int YDecimalPrecision
{
get { return _yDecimalPrecision; }
set { _yDecimalPrecision = value; }
}
/// <summary>
/// Scaling for the vertical values
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Vertical (y) scaling")]
[WebDescription("Scale factor for vertical values (e.g., 1000 for thousands)")]
public bool YNumberScale
{
get { return _yNumberScale; }
set { _yNumberScale = value; }
}
/// <summary>
/// Optional prefix for y axis value display (e.g., $)
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Vertical (y) prefix")]
[WebDescription("Prefix for vertical values (e.g., $))")]
public string YNumberPrefix
{
get { return _yNumberPrefix; }
set { _yNumberPrefix = value; }
}
/// <summary>
/// List of colors to use while charting
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Colors to use")]
[WebDescription("List of colors to use when charting")]
public string Colors
{
get
{
if (_colors == null)
{
_colors = Utils.GetDefaultColors();
}
return _colors;
}
set { _colors = value; }
}
/// <summary>
/// The HTML Id of the chart
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Chart HTML ID")]
[WebDescription("The Html ID that will identify the chart")]
public string ChartID
{
get
{
if (_chartId == null) // Chart is missing an HTML ID
{
// We generate a ID for the chart
_chartId = "chart" + Utils.GenerateRandomId().ToString();
}
return _chartId;
}
set { _chartId = value; }
}
/// <summary>
/// do we have to display custom button to export to excel ?
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Enable excel export")]
[WebDescription("Display a custom button to export the chart to excel")]
public bool ExcelExport
{
get { return _excelExport; }
set { _excelExport = value; }
}
/// <summary>
/// do we have to display custom button to export to xml ?
/// </summary>
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDisplayName("Enable XML export")]
[WebDescription("Display a custom button to export the chart to a xml file")]
public bool XMLExport
{
get { return _xmlExport; }
set { _xmlExport = value; }
}
public enum DiagnosticSetting { None, IncludeVersion, Trace };
private DiagnosticSetting _myDiagnostics = DiagnosticSetting.None;
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[System.ComponentModel.Category("Fusion Charts")]
[WebDescription("Level of diagnostic reporting (into comment in script in page source)")]
[Microsoft.SharePoint.WebPartPages.WebPartStorage(Microsoft.SharePoint.WebPartPages.Storage.Shared)]
public DiagnosticSetting Diagnostics
{
get { return _myDiagnostics; }
set { _myDiagnostics = value; }
}
#endregion
#region Constructors
public ChartFromList()
{
this.ExportMode = WebPartExportMode.All;
}
#endregion
#region Methods
/// <summary>
/// Create all your controls here for rendering.
/// Try to avoid using the RenderWebPart() method.
/// </summary>
protected override void CreateChildControls()
{
if (!_error)
{
try
{
// wire up PreRender for diagnostics
this.PreRender += new EventHandler(OnPreRender);
base.CreateChildControls();
// We first check all settings
CheckSettings();
ListReader rdr = new ListReader(this, _listName, _viewName);
rdr.FindListView();
CheckFieldNames(rdr);
// We transform the list into XML Input
string chartXmlContent = GetXmlFromListData(rdr);
// We generate the graph
string chartHtml = Utils.RenderChartHTML(ChartType, chartXmlContent, ChartID.ToString(), ChartWidth.ToString(), ChartHeight.ToString(), false);
LiteralControl lc = new LiteralControl(chartHtml);
this.Controls.Add(lc);
// We add a cariage return to display correctly export buttons
if (this._excelExport || this._xmlExport)
{
this.Controls.Add(new LiteralControl("<br />"));
}
// display (or not, depending of the user choice option) the button "export to excel"
if (this._excelExport)
{
Button btnExcel = new Button();
btnExcel.Text = "Export chart to Excel";
btnExcel.Click += new System.EventHandler(this.ExportExcelButtonClick);
btnExcel.Style.Add("font-size", "10");
btnExcel.Style.Add("margin", "10");
this.Controls.Add(btnExcel);
}
// display (or not, depending of the user choice option) the button "export to xml"
if (this._xmlExport)
{
Button btnXML = new Button();
btnXML.Text = "Export chart to XML file";
btnXML.Click += new System.EventHandler(this.ExportXmlButtonClick);
btnXML.Style.Add("font-size", "10");
btnXML.Style.Add("margin", "10");
this.Controls.Add(btnXML);
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
/// <summary>
/// Find requested list (& view), and read requested fields, and return plot data
/// </summary>
/// <returns></returns>
private Plots GetPlotData(ListReader rdr)
{
List<string> yFieldNames = new List<string>();
yFieldNames.Add(YValue);
Plots plots = rdr.GetPlots(XValue, yFieldNames, Action);
return plots;
}
/// <summary>
/// Get list data and write it to xml format used by Fusion Charts Free
/// </summary>
/// <returns></returns>
private string GetXmlFromListData(ListReader rdr)
{
// Get series data from list
Plots plots = GetPlotData(rdr);
// Write series data into XML string
ChartXmlWriter writer = new ChartXmlWriter(plots, ChartTitle, XTitle, YTitle, Colors);
writer.YDecimalPrecision = YDecimalPrecision;
writer.YNumberScale = YNumberScale;
writer.YNumberPrefix = YNumberPrefix;
string xml = writer.WriteXml();
return xml;
}
/// <summary>
/// Clear all child controls and add an error message for display.
/// </summary>
/// <param name="ex"></param>
private void HandleException(Exception ex)
{
this._error = true;
this.Controls.Clear();
this.Controls.Add(new LiteralControl("ERROR: " + ex.Message));
}
/// <summary>
/// After Load and PostBack events, PreRender occurs
/// </summary>
/// <param name="obj"></param>
/// <param name="args"></param>
private void OnPreRender(object obj, EventArgs args)
{
// Add trace info after all controls are loaded
// and all postback events have fired, so _myTrace
// has all trace info
OutputTrace();
}
/// <summary>
/// Checks the web part properties (settings) before trying to render the chart
/// </summary>
/// <returns></returns>
private void CheckSettings()
{
if (_listName == String.Empty)
{
throw (new Exception("Please edit the webpart properties to provide a SharePoint List name to use."));
}
if (_xValue == String.Empty)
{
throw (new Exception("Please edit the webpart properties to provide a horizontal (x) column name to use."));
}
if (_yValue == String.Empty && Action != GroupAction.COUNT)
{
throw (new Exception("Please edit the webpart properties to provide a vertical (y) column name to use."));
}
}
private void CheckFieldNames(ListReader rdr)
{
if (rdr.ParsedList == null)
{
throw (new Exception("Please edit the webpart properties to provide a valid SharePoint list name to use."));
}
if (rdr.ParsedView == null)
{
throw (new Exception("Please edit the webpart properties to provide a valid SharePoint view name to use."));
}
_xValue = GetInternalFieldName(rdr.ParsedList, _xValue,
"Please edit the webpart properties to provide a valid vertical (y) column name to use."
);
if (!rdr.ParsedView.ViewFields.Exists(_xValue))
{
throw (new Exception("Please edit the webpart properties to use a view that contains the horizontal (x) column chosen."));
}
if (Action != GroupAction.COUNT)
{
_yValue = GetInternalFieldName(rdr.ParsedList, _yValue,
"Please edit the webpart properties to provide a valid vertical (y) column name to use."
);
if (!rdr.ParsedView.ViewFields.Exists(_yValue))
{
throw (new Exception("Please edit the webpart properties to use a view that contains the vertical (y) column chosen."));
}
}
}
private string GetInternalFieldName(SPList list, string name, string errormsg)
{
SPField field = null;
try
{
field = list.Fields.GetFieldByInternalName(name);
}
catch
{
// try as display name
field = list.Fields[name];
}
if (field == null)
{
throw (new Exception(errormsg));
}
return field.InternalName;
}
/// <summary>
/// When the user click on the export to Excel button
/// so we build a csv and then make it download with good header to make it opened by excel automatically
/// </summary>
protected void ExportExcelButtonClick(object sender, EventArgs e)
{
// We first check all settings
CheckSettings();
ListReader rdr = new ListReader(this, _listName, _viewName);
rdr.FindListView();
CheckFieldNames(rdr);
// Get List data
Plots plots = GetPlotData(rdr);
// we build the csv string
StringBuilder csv = new StringBuilder();
csv.AppendLine(((XTitle != "") ? XTitle : "X") + ";" + ((YTitle != "") ? YTitle : "Y"));
for (int i = 0; i < plots.Count(); i++)
{
csv.AppendLine(plots.GetX(i).Replace(",", " ").Replace(";", " ") + ";" + plots.GetY(i).ToString().Replace(",", " ").Replace(";", " "));
}
Context.Response.Clear();
Context.Response.ContentType = "application/octet-stream";
Context.Response.AddHeader("Content-Disposition", "attachment; filename=ExcelExport.csv");
Context.Response.AddHeader("Content-Length", csv.Length.ToString());
Context.Response.Write(csv.ToString());
Context.Response.End();
}
/// <summary>
/// When the user click on the export to XML button
/// so we get to xml, and make it download
/// </summary>
protected void ExportXmlButtonClick(object sender, EventArgs e)
{
// We first check all settings
CheckSettings();
ListReader rdr = new ListReader(this, _listName, _viewName);
rdr.FindListView();
CheckFieldNames(rdr);
// We transform the list into XML Input
string chartXmlContent = GetXmlFromListData(rdr);
Context.Response.Clear();
Context.Response.ContentType = "application/octet-stream";
Context.Response.AddHeader("Content-Disposition", "attachment; filename=XMLExport.xml");
Context.Response.AddHeader("Content-Length", chartXmlContent.Length.ToString());
Context.Response.Write(chartXmlContent);
Context.Response.End();
}
#endregion
#region Developer Diagnostics
/// <summary>
/// Write diagnostic info into trace string
/// If user has enabled it, trace string will be written to an HTML comment inside page
/// </summary>
/// <param name="msg"></param>
public void TraceLine(string msg)
{
_myTrace += msg + "\n";
}
/// <summary>
/// Write version info (and accumulated trace info if any) into HTML comment in page
/// </summary>
private void OutputTrace()
{
if (_myDiagnostics != DiagnosticSetting.None)
{
string tracemsg = "";
if (_myDiagnostics == DiagnosticSetting.Trace)
{
tracemsg = "\nTrace:\n";
if (_myTrace.Length == 0)
{
tracemsg += "No Recorded Trace Info";
}
else
{
tracemsg += System.Web.HttpUtility.HtmlEncode(_myTrace);
}
tracemsg += "\n";
}
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
string message = "<!--\n "
+ "charts4sharepoint:\n"
+ "Now: " + DateTime.Now.ToString() + "\n"
+ "Build String: " + _myBuildVersion + "\n"
+ "Assembly: " + assembly.FullName + "\n"
+ "AssemblyFileVersion: " + System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion + "\n"
+ tracemsg
+ " -->\n";
this.Controls.Add(new LiteralControl(message));
}
}
#endregion
}
}