/// <summary> /// Parses the given command-line arguments. /// </summary> /// <param name="args">Arguments to be parsed.</param> /// <returns>An instance of Args containing the argument values; null if the argument list is invalid.</returns> internal static Args Parse(string[] args) { // // Valid argument count, length and prefixes? // if (!CheckArgs(args)) { return(null); } Args res = new Args(); // // Url shouldn't be empty and should start with either http:// or https://. // string url = FindArg(args, "url"); if (url == null || url.Length == 0) { return(null); } res.Url = url; url = url.ToLower(); if (!(url.StartsWith("http://") && url.Length > 7) && !(url.StartsWith("https://") && url.Length > 8)) { return(null); } // // List shouldn't be empty. // string list = FindArg(args, "list"); if (list == null || list.Length == 0) { return(null); } res.List = list; // // Output language can be set optionally and should be CS or VB (case-insensitive). // string language = FindArg(args, "language"); if (language != null && language.Length != 0) { language = language.ToUpper(); if (language != "CS" && language != "VB") { return(null); } else { res.Language = language; } } else { res.Language = "CS"; } // // Out can optionally be empty; in that case, take the lists's name suffixed with the language extension. // string file = FindArg(args, "out"); if (file == null || file.Length == 0) { file = list + (res.Language == "CS" ? ".cs" : ".vb"); foreach (char c in Path.GetInvalidFileNameChars()) { file = file.Replace(c.ToString(), ""); } } res.File = file; // // Only process authentication arguments if a user argument is present. // string user = FindArg(args, "user"); if (user != null && user.Length != 0) { // // Password required if user has been specified. Password can be the empty string. // string password = FindArg(args, "password"); if (password != null) { res.User = user; res.Password = password; // // Optional domain name. // string domain = FindArg(args, "domain"); if (domain != null && domain.Length != 0) { res.Domain = domain; } } else { return(null); } } return(res); }
/// <summary> /// Generates an entity based on the given arguments. /// </summary> /// <param name="a">Arguments specifying the list to be exported.</param> /// <returns>Entity object containing the code and other information about the exported entity; null if the export wasn't successful.</returns> public Entity Generate(Args a) { // // Set correct language code fragments. // if ((language = a.Language) == "CS") { ENUM = ENUM_CS; CLASS = CLASS_CS; PROP = PROP_CS; PROP_READONLY = PROP_READONLY_CS; CHOICEHELPERPROP = CHOICEHELPERPROP_CS; } else { ENUM = ENUM_VB; CLASS = CLASS_VB; PROP = PROP_VB; PROP_READONLY = PROP_READONLY_VB; CHOICEHELPERPROP = CHOICEHELPERPROP_VB; } // // List definition XML; will be downloaded from server. // XmlNode lst; // // Create proxy object referring to the SharePoint lists.asmx service on the specified server. // Lists l = new Lists(); l.Url = a.Url.TrimEnd('/') + "/_vti_bin/lists.asmx"; // // Try to connect to server. // try { // // Send event about connection. // EventHandler <ConnectingEventArgs> connecting = Connecting; if (connecting != null) { connecting(this, new ConnectingEventArgs(l.Url)); } // // Integrated authentication using current network credentials. // if (a.User == null) { l.Credentials = CredentialCache.DefaultNetworkCredentials; } // // Use specified credentials. // else { if (a.Domain == null) { l.Credentials = new NetworkCredential(a.User, a.Password); } else { l.Credentials = new NetworkCredential(a.User, a.Password, a.Domain); } } // // Send event about connection completion. // EventHandler <ConnectedEventArgs> connected = Connected; if (connected != null) { connected(this, new ConnectedEventArgs()); } } catch (Exception ex) { // // Send event about connection failure. // EventHandler <ConnectedEventArgs> connected = Connected; if (connected != null) { connected(this, new ConnectedEventArgs(ex)); } return(null); } try { // // Load schema from server using lists.asmx web service and send event about schema loading. // EventHandler <LoadingSchemaEventArgs> loadingSchema = LoadingSchema; if (loadingSchema != null) { loadingSchema(this, new LoadingSchemaEventArgs(a.List)); } lst = l.GetList(a.List); // // Send event about schema loading completion. // EventHandler <LoadedSchemaEventArgs> loadedSchema = LoadedSchema; if (loadedSchema != null) { loadedSchema(this, new LoadedSchemaEventArgs()); } } catch (Exception ex) { // // Send event about schema loading failure. // EventHandler <LoadedSchemaEventArgs> loadedSchema = LoadedSchema; if (loadedSchema != null) { loadedSchema(this, new LoadedSchemaEventArgs(ex)); } return(null); } // // Get general information of the list. // string listName = GetFriendlyName((string)lst.Attributes["Title"].Value); string listDescription = (string)lst.Attributes["Description"].Value; if (listDescription == "") { listDescription = null; } Guid listID = new Guid((string)lst.Attributes["ID"].Value); int version = int.Parse(lst.Attributes["Version"].Value); string path = (string)lst.Attributes["RootFolder"].Value; // // Send event about schema exporting. // EventHandler <ExportingSchemaEventArgs> exportingSchema = ExportingSchema; if (exportingSchema != null) { exportingSchema(this, new ExportingSchemaEventArgs(listName, listID, version)); } // // Get fields. // XmlElement fields = lst["Fields"]; StringBuilder props = new StringBuilder(); int n = 0; foreach (XmlNode c in fields.ChildNodes) { // // Field ID (GUID). // XmlAttribute aID = c.Attributes["ID"]; string id = ""; if (aID != null) { id = new Guid(aID.Value).ToString(); } // // Field name. // string name = (string)c.Attributes["Name"].Value; string displayName = (string)c.Attributes["DisplayName"].Value; // // Field description. // XmlAttribute aDescription = c.Attributes["Description"]; string description = null; if (aDescription != null) { description = (string)aDescription.Value; } // // Field hidden? // XmlAttribute aHidden = c.Attributes["Hidden"]; bool hidden = false; if (aHidden != null) { hidden = bool.Parse(aHidden.Value); } // // Field read-only? // XmlAttribute aReadOnly = c.Attributes["ReadOnly"]; bool readOnly = false; if (aReadOnly != null) { readOnly = bool.Parse(aReadOnly.Value); } // // Field type. // string type = (string)c.Attributes["Type"].Value; bool calc = false; // // Calculated field. Use underlying type for mapping. // if (type == "Calculated") { type = (string)c.Attributes["ResultType"].Value; calc = true; } // // Primary key field should be imported as well. // XmlAttribute primaryKey = c.Attributes["PrimaryKey"]; bool pk = false; if (primaryKey != null) { pk = bool.Parse(primaryKey.Value); } // // Export only fields that aren't hidden or the primary key field. // if (!hidden || pk) { // // Get underlying .NET type textual name for C# class generation. // Additional field might be required for multi-choice fields with fill-in choice. // bool additional; string bclType = GetType(c, out additional); // // Is the underlying type recognized and supported by the mapper? // if (bclType != null) { // // Read-only and calculated field require additional mapping attribute parameters. // StringBuilder extra = new StringBuilder(); if (pk) { extra.AppendFormat(", PrimaryKey{0}true", language == "CS" ? " = " : ":="); } if (readOnly) { extra.AppendFormat(", ReadOnly{0}true", language == "CS" ? " = " : ":="); } if (calc) { extra.AppendFormat(", Calculated{0}true", language == "CS" ? " = " : ":="); } // // Create helper field and refer to it in case a multi-choice fields with fill-in choice was detected. // The helper field has the same name as the .NET type (which will be an enum) suffixed with "Other". // string helper = null; if (additional) { helper = GetFriendlyName((string)c.Attributes["DisplayName"].Value) + "Other"; extra.AppendFormat(", OtherChoice{1}\"{0}\"", helper, language == "CS" ? " = " : ":="); } // // Lookup fields require a LookupField attribute property to be set. // if ((string)c.Attributes["Type"].Value == "Lookup" || (string)c.Attributes["Type"].Value == "LookupMulti") { extra.AppendFormat(", LookupField{1}\"{0}\"", (string)c.Attributes["ShowField"].Value, language == "CS" ? " = " : ":="); } // // LookupMulti fields shouldn't be settable. The underlying IList<T> type will allow changes to the collection though. // if ((string)c.Attributes["Type"].Value == "LookupMulti") { readOnly = true; } // // Generate a property for the current field and append it to the properties output string. // props.AppendFormat((readOnly ? PROP_READONLY : PROP), (description ?? displayName), bclType, GetFriendlyName(displayName), XmlConvert.DecodeName(name), type, id, extra.ToString()); // // Generate additional helper property if needed. // if (additional) { props.AppendFormat(CHOICEHELPERPROP, displayName, helper, XmlConvert.DecodeName(name), id); } // // Keep field count. // n++; } } } // // Send event about schema exporting completion. // EventHandler <ExportedSchemaEventArgs> exportedSchema = ExportedSchema; if (exportedSchema != null) { exportedSchema(this, new ExportedSchemaEventArgs(n)); } // // Build code with class definition containing the properties and the helper enums. // StringBuilder output = new StringBuilder(); output.AppendFormat(CLASS, listName, props.ToString(), listName, listID.ToString(), version, (listDescription ?? listName), path); // // Return entity. // Entity entity = new Entity(); entity.Name = listName; entity.Id = listID; entity.Code = output.ToString(); entity.Lookups = lookups; return(entity); }
/// <summary> /// Entry point for SpMetal. /// </summary> /// <param name="args">Command-line arguments.</param> static void Main(string[] args) { // // Title including assembly version number. // Console.WriteLine("Bart De Smet SpMetal SharePoint List Definition Export version {0}", Assembly.GetEntryAssembly().GetName().Version); Console.WriteLine("Copyright (C) Bart De Smet 2007. All rights reserved.\n"); // // Parse arguments. // Args a = Args.Parse(args); // // Invalid arguments: display help information and exit. // if (a == null) { Console.WriteLine("No inputs specified\n"); string file = Assembly.GetEntryAssembly().GetName().Name + ".exe"; Console.WriteLine("Usage: {0} -url:<url> -list:<list> [-out:<file>] [-language:<language>]", file); Console.WriteLine(" {0} [-user:<user> -password:<password> [-domain:<domain>]]", new string(' ', file.Length)); Console.WriteLine(); Console.WriteLine(" -url:<url> URL to the root of the SharePoint site"); Console.WriteLine(" -list:<list> Name of the list"); Console.WriteLine(" -out:<file> Output file"); Console.WriteLine(" -language:<language> Code language used for output (VB or CS)"); Console.WriteLine(" (Default: CS)"); Console.WriteLine(); Console.WriteLine(" -user:<user> User name for connection to SharePoint site"); Console.WriteLine(" -password:<password> Password for connection to SharePoint site"); Console.WriteLine(" -domain:<domain> Domain for connection to SharePoint site"); return; } // // Set correct language code fragments. // if ((language = a.Language) == "CS") { ENUM = ENUM_CS; CLASS = CLASS_CS; PROP = PROP_CS; CHOICEHELPERPROP = CHOICEHELPERPROP_CS; } else { ENUM = ENUM_VB; CLASS = CLASS_VB; PROP = PROP_VB; CHOICEHELPERPROP = CHOICEHELPERPROP_VB; } // // List definition XML; will be downloaded from server. // XmlNode lst; // // Try to connect to server. // try { Console.Write("Connecting to server... "); // // Create proxy object referring to the SharePoint lists.asmx service on the specified server. // Lists l = new Lists(); l.Url = a.Url.TrimEnd('/') + "/_vti_bin/lists.asmx"; // // Integrated authentication using current network credentials. // if (a.User == null) { l.Credentials = CredentialCache.DefaultNetworkCredentials; } // // Use specified credentials. // else { if (a.Domain == null) { l.Credentials = new NetworkCredential(a.User, a.Password); } else { l.Credentials = new NetworkCredential(a.User, a.Password, a.Domain); } } Console.WriteLine("Done"); // // Load schema from server using lists.asmx web service. // Console.Write("Loading schema... "); lst = l.GetList(a.List); Console.WriteLine("Done\n"); } catch (Exception ex) { Console.WriteLine("Failed\n"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(ex.Message); Console.ResetColor(); Environment.Exit(-1); return; } // // Get general information of the list. // string listName = GetFriendlyName((string)lst.Attributes["Title"].Value); string listDescription = (string)lst.Attributes["Description"].Value; if (listDescription == "") { listDescription = null; } Guid listID = new Guid((string)lst.Attributes["ID"].Value); int version = int.Parse(lst.Attributes["Version"].Value); string path = (string)lst.Attributes["RootFolder"].Value; Console.Write("Processing list {0} ({1}) version {2}... ", listName, listID, version); // // Get fields. // XmlElement fields = lst["Fields"]; StringBuilder props = new StringBuilder(); int n = 0; foreach (XmlNode c in fields.ChildNodes) { // // Field ID (GUID). // XmlAttribute aID = c.Attributes["ID"]; string id = ""; if (aID != null) { id = new Guid(aID.Value).ToString(); } // // Field name. // string name = (string)c.Attributes["Name"].Value; string displayName = (string)c.Attributes["DisplayName"].Value; // // Field description. // XmlAttribute aDescription = c.Attributes["Description"]; string description = null; if (aDescription != null) { description = (string)aDescription.Value; } // // Field hidden? // XmlAttribute aHidden = c.Attributes["Hidden"]; bool hidden = false; if (aHidden != null) { hidden = bool.Parse(aHidden.Value); } // // Field read-only? // XmlAttribute aReadOnly = c.Attributes["ReadOnly"]; bool readOnly = false; if (aReadOnly != null) { readOnly = bool.Parse(aReadOnly.Value); } // // Field type. // string type = (string)c.Attributes["Type"].Value; bool calc = false; // // Calculated field. Use underlying type for mapping. // if (type == "Calculated") { type = (string)c.Attributes["ResultType"].Value; calc = true; } // // Field inherited from base type? // XmlAttribute aFromBaseType = c.Attributes["FromBaseType"]; bool fromBaseType = false; if (aFromBaseType != null) { fromBaseType = bool.Parse(aFromBaseType.Value); } // // Export only field that aren't hidden and aren't inherited from the base type. // if (!hidden && !fromBaseType) { // // Get underlying .NET type textual name for C# class generation. // Additional field might be required for multi-choice fields with fill-in choice. // bool additional; string bclType = GetType(c, out additional); // // Is the underlying type recognized and supported by the mapper? // if (bclType != null) { // // Read-only and calculated field require additional mapping attribute parameters. // StringBuilder extra = new StringBuilder(); if (readOnly) { extra.AppendFormat(", ReadOnly{0}true", language == "CS" ? " = " : ":="); } if (calc) { extra.AppendFormat(", Calculated{0}true", language == "CS" ? " = " : ":="); } // // Create helper field and refer to it in case a multi-choice fields with fill-in choice was detected. // The helper field has the same name as the .NET type (which will be an enum) suffixed with "Other". // string helper = null; if (additional) { helper = GetFriendlyName((string)c.Attributes["DisplayName"].Value) + "Other"; extra.AppendFormat(", OtherChoice{1}\"{0}\"", helper, language == "CS" ? " = " : ":="); } // // Generate a property for the current field and append it to the properties output string. // props.AppendFormat(PROP, (description ?? displayName), bclType, GetFriendlyName(displayName), XmlConvert.DecodeName(name), type, id, extra.ToString()); // // Generate additional helper property if needed. // if (additional) { props.AppendFormat(CHOICEHELPERPROP, displayName, helper, XmlConvert.DecodeName(name), id); } // // Keep field count. // n++; } } } // // Print statistical information. // Console.WriteLine("Done"); Console.WriteLine("Exported {0} properties and {1} helper enums", n, enums.Count); Console.WriteLine(); // // Build file with class definition containing the properties and the helper enums. // Console.Write("Writing file {0}... ", a.File); StringBuilder output = new StringBuilder(); output.AppendFormat(CLASS, listName, props.ToString(), listName, listID.ToString(), version, (listDescription ?? listName), path); foreach (string e in enums) { output.Append(e); } // // Write to output file. // using (StreamWriter sw = File.CreateText(a.File)) { sw.WriteLine(output.ToString()); Console.WriteLine("Done"); } }
/// <summary> /// Entry point for SpMetal. /// </summary> /// <param name="args">Command-line arguments.</param> static void Main(string[] args) { // // Title including assembly version number. // Console.WriteLine("Bart De Smet SpMetal SharePoint List Definition Export version {0}", Assembly.GetEntryAssembly().GetName().Version); Console.WriteLine("Copyright (C) Bart De Smet 2007. All rights reserved.\n"); // // Parse arguments. // Args a = Args.Parse(args); // // Invalid arguments: display help information and exit. // if (a == null) { Console.WriteLine("No inputs specified\n"); string file = Assembly.GetEntryAssembly().GetName().Name + ".exe"; Console.WriteLine("Usage: {0} -url:<url> -list:<list> [-out:<file>] [-language:<language>]", file); Console.WriteLine(" {0} [-user:<user> -password:<password> [-domain:<domain>]]", new string(' ', file.Length)); Console.WriteLine(); Console.WriteLine(" -url:<url> URL to the root of the SharePoint site"); Console.WriteLine(" -list:<list> Name of the list"); Console.WriteLine(" -out:<file> Output file"); Console.WriteLine(" -language:<language> Code language used for output (VB or CS)"); Console.WriteLine(" (Default: CS)"); Console.WriteLine(); Console.WriteLine(" -user:<user> User name for connection to SharePoint site"); Console.WriteLine(" -password:<password> Password for connection to SharePoint site"); Console.WriteLine(" -domain:<domain> Domain for connection to SharePoint site"); return; } // // Entity generator. // EntityGenerator gen = new EntityGenerator(); gen.Connecting += delegate(object sender, ConnectingEventArgs e) { Console.Write("Connecting to server... "); }; gen.Connected += delegate(object sender, ConnectedEventArgs e) { if (e.Succeeded) { Console.WriteLine("Done"); } else { Console.WriteLine("Failed\n"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(e.Exception.Message); Console.ResetColor(); Environment.Exit(-1); } }; gen.LoadingSchema += delegate(object sender, LoadingSchemaEventArgs e) { Console.Write("Loading schema... "); }; gen.LoadedSchema += delegate(object sender, LoadedSchemaEventArgs e) { if (e.Succeeded) { Console.WriteLine("Done\n"); } else { Console.WriteLine("Failed\n"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(e.Exception.Message); Console.ResetColor(); Environment.Exit(-1); } }; gen.ExportingSchema += delegate(object sender, ExportingSchemaEventArgs e) { Console.Write("Processing list {0} ({1}) version {2}... ", e.List, e.Identifier, e.Version); }; gen.ExportedSchema += delegate(object sender, ExportedSchemaEventArgs e) { if (e.Succeeded) { Console.WriteLine("Done"); Console.WriteLine("Exported {0} properties\n", e.PropertyCount); } else { Console.WriteLine("Failed\n"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(e.Exception.Message); Console.ResetColor(); Environment.Exit(-1); } }; // // Create a queue of entities that allows to process Lookup field entities. // Queue <Entity> entities = new Queue <Entity>(); HashSet <string> encounteredEntities = new HashSet <string>(); Dictionary <Guid, string> map = new Dictionary <Guid, string>(); // // Generate the requested entity. // HashSet <string> forbiddenTypeNames = new HashSet <string>(); Entity entity = gen.Generate(a); // // Infer file name from list name if not specified on the command-line (v0.2.0.0). // if (a.File == null || a.File.Length == 0) { string file = entity.Name + (a.Language == "CS" ? ".cs" : ".vb"); foreach (char c in Path.GetInvalidFileNameChars()) { file = file.Replace(c.ToString(), ""); } a.File = file; } // // Build the code; import the namespaces required. // StringBuilder allCode = new StringBuilder(); if (a.Language == "CS") { allCode.Append(IMPORTS_CS); } else { allCode.Append(IMPORTS_VB); } // // Resolve all Lookup field references, recursively. // entities.Enqueue(entity); map.Add(entity.Id, entity.Name); //Patch for lists that reference themselves. No need to re-generate entity type. encounteredEntities.Add(entity.Name); while (entities.Count > 0) { entity = entities.Dequeue(); // // Patch the entity's Lookup fields by generating sub-entities. // StringBuilder code = new StringBuilder(); code.Append(entity.Code); foreach (string patch in entity.Lookups.Keys) { string lookupList = entity.Lookups[patch]; string lookupEntityName; if (map.ContainsKey(new Guid(lookupList))) { lookupEntityName = map[new Guid(lookupList)]; } else { a.List = lookupList; Entity lookupEntity = gen.Generate(a); lookupEntityName = lookupEntity.Name; map.Add(lookupEntity.Id, lookupEntity.Name); entities.Enqueue(lookupEntity); } code.Replace(patch, lookupEntityName); } entity.Code = code.ToString(); // // Append the code to the accumulated code buffer. // allCode.Append(entity.Code); } foreach (string e in gen.Enums) { allCode.Append(e); } // // Write to output file. // Console.Write("Writing file {0}... ", a.File); using (StreamWriter sw = File.CreateText(a.File)) { sw.WriteLine(allCode.ToString()); Console.WriteLine("Done"); } }