Пример #1
0
        private void CleanFields(SharePointSession session, FieldCollection fields)
        {
            if (this.FieldGroup == null)
            {
                return;
            }
            ClientContext clientContext = session.ClientContext;

            clientContext.Load(fields, c => c.Include(t => t.Group, t => t.StaticName));
            session.ExecuteQuery();

            List <Field> tbdFields = new List <Field>();

            foreach (Field f in fields)
            {
                if (f.Group == this.FieldGroup)
                {
                    tbdFields.Add(f);
                }
            }
            if (tbdFields.Count > 0)
            {
                foreach (Field f in tbdFields)
                {
                    f.DeleteObject();
                }
                session.ExecuteQuery();
            }
        }
Пример #2
0
        private void ApplyMetadata(FolderInfo folder, ProgressTracker tracker)
        {
            // Apply the actual metadata to the folder, including permissions, attributes, etc.
            using (ObjectPool <SharePointSession> .Ref sessionRef = this.ImportContext.SessionFactory.GetSession())
            {
                SharePointSession session       = sessionRef.Target;
                ClientContext     clientContext = session.ClientContext;
                XElement          xml           = XElement.Load(folder.Location);
                XNamespace        ns            = xml.GetDefaultNamespace();
                Folder            f             = clientContext.Web.GetFolderByServerRelativeUrl(folder.Url);
                tracker.TrackProgress("Gathering metadata and permissions for folder at [{0}]", folder.SafeFullPath);
                ContentType contentType = ResolveContentType(folder.Type);
                if (contentType != null)
                {
                    f.ListItemAllFields["ContentTypeId"] = contentType.Id;
                }

                FolderInfo parent        = ResolveFolder(folder.Path);
                bool       individualAcl = (parent == null || (folder.Acl != parent.Acl));
                ApplyMetadata(f.ListItemAllFields, xml);
                string aclResult = "inherited";
                if (individualAcl)
                {
                    aclResult = "independent";
                    f.ListItemAllFields.BreakRoleInheritance(false, false);
                    ApplyPermissions(f.ListItemAllFields, xml);
                }
                tracker.TrackProgress("The ACL for [{0}] will be {1} from its parent's", folder.SafeFullPath, aclResult);
                SetAuthorAndEditor(f.ListItemAllFields, xml);
                if (individualAcl)
                {
                    ApplyOwnerPermission(f.ListItemAllFields, xml);
                }
                f.ListItemAllFields.Update();
                f.Update();
                tracker.TrackProgress("Applying metadata and permissions on folder at [{0}]", folder.SafeFullPath);
                session.ExecuteQuery();
                ShowProgress();
            }
        }
Пример #3
0
        public static int MainLoop(string[] args)
        {
            ILog   log     = null;
            string baseDir = Directory.GetCurrentDirectory();
            // Initialize log4j
            Configuration options = new Configuration(baseDir, args);

            if (options.help)
            {
                Console.Error.WriteLine(options.GetUsage());
                return(1);
            }
            List <string> errors = options.ValidateConfiguration();

            if (errors.Count > 0)
            {
                Console.Error.WriteLine(string.Format("{0} Configuration Errors detected:", errors.Count));
                foreach (string e in errors)
                {
                    Console.Error.WriteLine(string.Format("\t* {0}", e));
                }
                return(2);
            }

            System.IO.Directory.CreateDirectory(options.caches);

            string logDir = string.Format("{0}\\logs", baseDir);

            System.IO.Directory.CreateDirectory(logDir);

            Environment.SetEnvironmentVariable("CMF_LOGDATE", string.Format("{0:yyyyMMdd-HHmmss}", DateTime.Now));
            Environment.SetEnvironmentVariable("CMF_LOGDIR", logDir);

            XmlConfigurator.Configure(new FileInfo(string.Format("{0}\\log4net.xml", baseDir)));
            LOG = log = LogManager.GetLogger(typeof(Launcher));
            log.Info("Initializing Application");

            if (options.indexOnly)
            {
                ImportContext  importContext  = new ImportContext(null, options.content, options.metadata, options.caches);
                FormatResolver formatResolver = new FormatResolver(importContext);
                new DocumentImporter(new FolderImporter(importContext), formatResolver, options.locationMode, options.fixExtensions).StoreLocationIndex();
                return(0);
            }

            ServicePointManager.MaxServicePointIdleTime = (int)SharePointSession.TIME_OUT.TotalMilliseconds;
            ServicePointManager.SetTcpKeepAlive(true, (int)SharePointSession.TIME_OUT.TotalMilliseconds, 60000);
            ServicePointManager.DefaultConnectionLimit = options.threads * 10;

            using (DirectoryEntry ldapDirectory = BindToLDAP(options))
            {
                log.Info(string.Format("Using SharePoint at [{0}]", options.siteUrl));

                string userString = options.user;
                if (!string.IsNullOrWhiteSpace(options.domain))
                {
                    userString = string.Format("{0}@{1}", userString, options.domain);
                }

                SecureString userPassword = null;
                if (!string.IsNullOrWhiteSpace(options.user))
                {
                    if (options.password == null)
                    {
                        Console.Write(string.Format("Enter The Sharepoint Password for [{0}]: ", userString));
                        userPassword = Tools.ReadPassword();
                    }
                    else
                    {
                        String pass = CRYPT.Decrypt(options.password);
                        pass = CRYPT.Encrypt(pass);
                        log.Info(string.Format("Using stored credentials for [{0}] = [{1}]", userString, pass));
                        userPassword = new SecureString();
                        foreach (char c in CRYPT.Decrypt(pass))
                        {
                            userPassword.AppendChar(c);
                        }
                    }
                }

                using (SharePointSessionFactory sessionFactory = new SharePointSessionFactory(new SharePointSessionInfo(options.siteUrl, options.user, userPassword, options.domain, options.applicationId, options.certificateKey, options.certificatePass, options.library, options.reuseCount)))
                {
                    ImportContext importContext = new ImportContext(sessionFactory, options.content, options.metadata, options.caches);
                    using (ObjectPool <SharePointSession> .Ref sessionRef = sessionFactory.GetSession())
                    {
                        SharePointSession session       = sessionRef.Target;
                        ClientContext     clientContext = session.ClientContext;
                        List documentLibrary            = sessionRef.Target.DocumentLibrary;
                        // We configure the document library as required
                        documentLibrary.EnableVersioning    = true;
                        documentLibrary.EnableMinorVersions = true;
                        documentLibrary.ForceCheckout       = false;
                        documentLibrary.ContentTypesEnabled = true;
                        // documentLibrary.MajorVersionLimit = 50000;
                        // documentLibrary.MajorWithMinorVersionsLimit = 50000;
                        documentLibrary.Update();
                        session.ExecuteQuery();
                    }

                    FormatResolver formatResolver = new FormatResolver(importContext);

                    ContentTypeImporter contentTypeImporter = null;
                    for (int i = 0; i <= options.retries; i++)
                    {
                        try
                        {
                            contentTypeImporter = new ContentTypeImporter(importContext, options.library, options.cleanTypes);
                            break;
                        }
                        catch (Exception e)
                        {
                            contentTypeImporter = null;
                            log.Warn("WARNING: ContentTypeImporter failed to initialize due to an exception", e);
                        }
                    }
                    if (contentTypeImporter == null)
                    {
                        log.Error(string.Format("ContentTypeImporter failed to initialize after {0} attempts", options.retries + 1));
                        return(3);
                    }

                    UserGroupImporter userGroupImporter = null;
                    for (int i = 0; i <= options.retries; i++)
                    {
                        try
                        {
                            userGroupImporter = new UserGroupImporter(importContext, ldapDirectory, options.ldapSyncDomain, options.fallbackUser, options.internalUser, options.fallbackGroup, options.internalGroup);
                            break;
                        }
                        catch (Exception e)
                        {
                            userGroupImporter = null;
                            log.Warn("WARNING: UserGroupImporter failed to initialize due to an exception", e);
                        }
                    }
                    if (userGroupImporter == null)
                    {
                        log.Error(string.Format("UserGroupImporter failed to initialize after {0} attempts", options.retries + 1));
                        return(4);
                    }

                    PermissionsImporter permissionsImporter = new PermissionsImporter(userGroupImporter);
                    FolderImporter      folderImporter      = new FolderImporter(contentTypeImporter, permissionsImporter);
                    DocumentImporter    documentImporter    = new DocumentImporter(folderImporter, formatResolver, options.locationMode, options.fixExtensions);
                    bool aborted = false;

                    Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
                    {
                        if (aborted)
                        {
                            return;
                        }
                        aborted  = true;
                        e.Cancel = true;
                        documentImporter.Abort = true;
                        folderImporter.Abort   = true;
                        string msg = "Program Interrupted (hit Ctrl-C again to terminate immediately)";
                        if (log == null)
                        {
                            Console.WriteLine(msg);
                        }
                        else
                        {
                            log.Warn(msg);
                        }
                    };

                    try
                    {
                        documentImporter.StoreDocuments(options.threads, options.simulationMode, options.locationMode, options.autoPublish, options.retries);
                        folderImporter.FinalizeFolders(options.threads, options.retries);
                        documentImporter.StoreLocationIndex();
                    }
                    finally
                    {
                        log.Info(documentImporter.ProcessingReport);
                        log.Info(folderImporter.ProcessingReport);
                    }
                    return(aborted ? 1 : 0);
                }
            }
        }
Пример #4
0
 public async Task SaveSharePointSession(Guid sessionId, SharePointSession sharePointSession)
 {
     await SetAsync(sessionId.ToString(), sharePointSession, AuthenticationParameters.CacheSessionDurationInMinutes);
 }
Пример #5
0
        private void ProcessAccumulatedFolders(SharePointSession session, Dictionary <string, List <FolderInfo> > folders)
        {
            // This allows for the case where we're only creating indexes, and don't want to touch the repository
            if (session == null)
            {
                return;
            }
            ClientContext clientContext = session.ClientContext;
            Folder        rootFolder    = session.RootFolder;

            // We know all of these folders are at the same depth, so for all of these we only need
            // to find their parent folder(s), and add the new folders
            Web web = clientContext.Web;
            // Step 1: gather all the parent folders in this batch
            Dictionary <string, Folder> spFolders = new Dictionary <string, Folder>();
            List <FolderInfo>           processed = new List <FolderInfo>();

            foreach (string parent in folders.Keys)
            {
                Folder f = null;
                if (parent == "")
                {
                    f = rootFolder;
                }
                else
                {
                    f = web.GetFolderByServerRelativeUrl(rootFolder.ServerRelativeUrl + parent);
                }
                clientContext.Load(f, r => r.ServerRelativeUrl);
                spFolders[parent] = f;
            }
            // Now get all the parent folders in a single query
            session.ExecuteQuery();

            // Step 2: go through all the parent folders, and create their children
            int batch = 0;

            foreach (string parent in folders.Keys)
            {
                Folder parentFolder = spFolders[parent];
                foreach (FolderInfo f in folders[parent])
                {
                    ProgressTracker tracker = new ProgressTracker(f.Location, this.Log);
                    if (tracker.Completed)
                    {
                        f.SPObj  = parentFolder.Folders.GetByUrl(f.SafeName);
                        f.Exists = true;
                        Log.Info(string.Format("Loaded existing folder [{0}] from [{1}]", f.FullPath, f.SafeFullPath));
                    }
                    else
                    {
                        f.SPObj    = parentFolder.Folders.Add(f.SafeName);
                        f.Modified = true;
                        Log.Info(string.Format("Added folder [{0}] for creation as [{1}]", f.FullPath, f.SafeFullPath));
                    }
                    clientContext.Load(f.SPObj, r => r.ServerRelativeUrl);
                    processed.Add(f);
                    // Do them in bunches of 50
                    if (++batch >= 50)
                    {
                        Log.Info(string.Format("Executing the creation of the last {0} folders", batch));
                        session.ExecuteQuery();
                        batch = 0;
                    }
                }
            }
            // Now, create all the children in a single query
            if (batch > 0)
            {
                Log.Info(string.Format("Executing the creation of the last {0} folders", batch));
                session.ExecuteQuery();
            }
            foreach (FolderInfo f in processed)
            {
                // Update the server relative URLs for each of them
                f.Url = f.SPObj.ServerRelativeUrl;
            }
        }
Пример #6
0
        private FolderImporter(ImportContext importContext, ContentTypeImporter contentTypeImporter, PermissionsImporter permissionsImporter) : base("folders", importContext, contentTypeImporter, permissionsImporter)
        {
            Dictionary <string, FolderInfo> folderDictionary = new Dictionary <string, FolderInfo>();

            using (XmlReader folders = this.ImportContext.LoadIndex("folders"))
            {
                int currentDepth = 0;
                Dictionary <string, List <FolderInfo> > accumulated = new Dictionary <string, List <FolderInfo> >();
                int accumulatedCount = 0;

                using (ObjectPool <SharePointSession> .Ref sessionRef = this.ImportContext.SessionFactory?.GetSession())
                {
                    SharePointSession session = sessionRef?.Target;
                    outer : while (folders.ReadToFollowing("folder"))
                    {
                        string location = null;
                        string path     = null;
                        using (XmlReader folder = folders.ReadSubtree())
                        {
                            // We're only interested in the <location> and <path> elements, so if they're not there
                            // we simply move on to the next folder
                            if (!folder.ReadToFollowing("path"))
                            {
                                goto outer;
                            }
                            path = "/" + folder.ReadElementContentAsString();
                            if (!folder.ReadToFollowing("location"))
                            {
                                goto outer;
                            }
                            location = folder.ReadElementContentAsString();
                        }

                        int thisDepth = (path == "/" ? 0 : thisDepth = Tools.CountChars(path, '/'));

                        // If we've changed depths, we process what we've accumulated so far
                        if (thisDepth > currentDepth)
                        {
                            if (session != null)
                            {
                                Log.Info(string.Format("Creating {0} folders in the target environment, depth {1}", accumulatedCount, thisDepth));
                                try
                                {
                                    ProcessAccumulatedFolders(session, accumulated);
                                }
                                catch (Exception e)
                                {
                                    Log.Error("Failed to process the current accumulated folder batch");
                                    throw e;
                                }
                            }
                            accumulated.Clear();
                            accumulatedCount = 0;
                            currentDepth     = thisDepth;
                        }

                        // A new folder to handle...
                        FolderInfo f = new FolderInfo(this.ImportContext.FormatMetadataLocation(location));

                        List <FolderInfo> l = null;
                        if (!accumulated.ContainsKey(f.SafePath))
                        {
                            l = new List <FolderInfo>();
                            accumulated[f.SafePath] = l;
                        }
                        else
                        {
                            l = accumulated[f.SafePath];
                        }

                        /*
                         * if (thisDepth == 0)
                         * {
                         *  // Check to see if this is a cabinet we want to avoid
                         *  if (XmlConvert.ToBoolean(XmlTools.GetAttributeValue(xml, "dctm:is_private")))
                         *  {
                         *      Log.Info(string.Format("Skipping private cabinet [{0}]", f.FullPath));
                         *      continue;
                         *  }
                         * }
                         */
                        l.Add(f);
                        accumulatedCount++;
                        folderDictionary[f.FullPath] = f;
                    }
                    if ((session != null) && accumulatedCount > 0)
                    {
                        Log.Info(string.Format("Creating {0} folders in the target environment, depth {1}", accumulated.Count, currentDepth + 1));
                        try
                        {
                            ProcessAccumulatedFolders(session, accumulated);
                        }
                        catch (Exception e)
                        {
                            Log.Error("Failed to process the last accumulated folder batch");
                            throw e;
                        }
                    }
                }
            }

            this.Folders = folderDictionary;
        }
Пример #7
0
        private void CleanContentTypes(SharePointSession session, ContentTypeCollection contentTypes)
        {
            if (this.TypeGroup == null)
            {
                return;
            }
            ClientContext clientContext = session.ClientContext;

            clientContext.Load(contentTypes, c => c.Include(t => t.Id, t => t.Name, t => t.Group, t => t.Parent, t => t.Parent.Name, t => t.Parent.Group));
            session.ExecuteQuery();

            Dictionary <string, ContentType> tbdTypes = new Dictionary <string, ContentType>();

            foreach (ContentType type in contentTypes)
            {
                if (type.Group == this.TypeGroup)
                {
                    tbdTypes[type.Name] = type;
                }
            }
            if (tbdTypes.Count > 0)
            {
                // TODO: we have an inheritance issue here, in that parent types can't be
                // deleted prior to their subtypes...so build the tree...
                Dictionary <int, List <ContentType> > tree = new Dictionary <int, List <ContentType> >();
                int maxDepth = -1;
                foreach (ContentType type in tbdTypes.Values)
                {
                    // Calculate the depth for this item
                    int         depth   = 0;
                    ContentType current = type;
                    while (current.Parent != null && current.Parent.Name != current.Name && current.Parent.Group == this.TypeGroup)
                    {
                        depth++;
                        if (!tbdTypes.ContainsKey(current.Parent.Name))
                        {
                            break;
                        }
                        current = tbdTypes[current.Parent.Name];
                    }
                    List <ContentType> l = null;
                    if (tree.ContainsKey(depth))
                    {
                        l = tree[depth];
                    }
                    else
                    {
                        l           = new List <ContentType>();
                        tree[depth] = l;
                    }
                    l.Add(type);
                    if (depth > maxDepth)
                    {
                        maxDepth = depth;
                    }
                }
                for (int d = maxDepth; d >= 0; d--)
                {
                    foreach (ContentType type in tree[d])
                    {
                        type.DeleteObject();
                    }
                }
                session.ExecuteQuery();
            }
        }
Пример #8
0
        public ContentTypeImporter(ImportContext importContext, String documentLibraryName, bool clearFirst) : base("content types", importContext)
        {
            this.Log.Info(string.Format("Mapping the object types to content types and columns to the site and the library [{0}]...", documentLibraryName));

            // TODO: Make these two configurable
            this.TypeGroup  = TYPE_GROUP;
            this.FieldGroup = FIELD_GROUP;

            using (ObjectPool <SharePointSession> .Ref sessionRef = this.ImportContext.SessionFactory.GetSession())
            {
                SharePointSession session       = sessionRef.Target;
                ClientContext     clientContext = session.ClientContext;
                List documentLibrary            = clientContext.Web.Lists.GetByTitle(documentLibraryName);
                if (clearFirst)
                {
                    try
                    {
                        Log.Warn("Cleaning out document library content types...");
                        CleanContentTypes(sessionRef.Target, documentLibrary.ContentTypes);
                        Log.Warn("Cleaning out document library fields...");
                        CleanFields(sessionRef.Target, documentLibrary.Fields);
                        Log.Warn("Cleaning out site content types...");
                        CleanContentTypes(sessionRef.Target, clientContext.Web.ContentTypes);
                        Log.Warn("Cleaning out site fields...");
                        CleanFields(sessionRef.Target, clientContext.Web.Fields);
                        Log.Warn("Fields and content types cleared!");
                    }
                    catch (Exception e)
                    {
                        Log.Warn("Tried to remove the existing content types, but failed", e);
                    }
                }

                ContentTypeCollection contentTypeCollection = clientContext.Web.ContentTypes;
                clientContext.Load(contentTypeCollection, c => c.Include(t => t.Id, t => t.Name, t => t.Group, t => t.Parent, t => t.FieldLinks));
                clientContext.Load(documentLibrary.ContentTypes, c => c.Include(t => t.Id, t => t.Name, t => t.Parent));
                clientContext.Load(clientContext.Web, w => w.Fields, w => w.AvailableFields);
                session.ExecuteQuery();

                // First we gather up whatever's already there
                Dictionary <string, ImportedContentType> siteContentTypes    = new Dictionary <string, ImportedContentType>();
                Dictionary <string, ImportedContentType> libraryContentTypes = new Dictionary <string, ImportedContentType>();
                Dictionary <string, ImportedContentType> contentTypesById    = new Dictionary <string, ImportedContentType>();
                Dictionary <string, ContentType>         allTypes            = new Dictionary <string, ContentType>();
                foreach (ContentType type in contentTypeCollection)
                {
                    ImportedContentType ct = new ImportedContentType(this, type);
                    siteContentTypes[type.Name]           = ct;
                    contentTypesById[type.Id.StringValue] = ct;
                }

                Dictionary <string, Field> existingFields = new Dictionary <string, Field>();
                foreach (Field f in clientContext.Web.Fields)
                {
                    existingFields[f.StaticName] = f;
                }

                HashSet <string> documentLibraryTypes = new HashSet <string>();
                foreach (ContentType ct in documentLibrary.ContentTypes)
                {
                    documentLibraryTypes.Add(ct.Name);
                    ImportedContentType ict = new ImportedContentType(this, ct);
                    libraryContentTypes[ct.Name]        = ict;
                    contentTypesById[ct.Id.StringValue] = ict;
                }

                // Now we go over the XML declarations
                HashSet <string> newTypes = new HashSet <string>();
                XElement         types    = XElement.Load(this.ImportContext.LoadIndex("types"));
                XNamespace       ns       = types.GetDefaultNamespace();
                foreach (XElement type in types.Elements(ns + "type"))
                {
                    string typeName = (string)type.Element(ns + "name");
                    ImportedContentType finalType = null;
                    HashSet <string>    linkNames = new HashSet <string>();
                    bool skipInherited            = true;
                    bool patchFields     = false;
                    bool versionableType = false;
                    bool containerType   = false;
                    if (siteContentTypes.ContainsKey(typeName))
                    {
                        finalType = siteContentTypes[typeName];
                        foreach (FieldLink link in finalType.Type.FieldLinks)
                        {
                            linkNames.Add(link.Name);
                        }
                        if (typeName == "dm_sysobject" || typeName == "dm_folder")
                        {
                            patchFields     = true;
                            versionableType = (typeName == "dm_sysobject");
                            containerType   = (typeName == "dm_folder");
                        }
                    }
                    else
                    {
                        // New type...create it
                        ImportedContentType superType        = null;
                        XElement            superTypeElement = type.Element(ns + "superType");
                        if ((superTypeElement != null) && (typeName != "dm_folder"))
                        {
                            string stName = (string)superTypeElement;
                            if (siteContentTypes.ContainsKey(stName))
                            {
                                superType = siteContentTypes[stName];
                            }
                        }

                        if (superType == null)
                        {
                            switch (typeName)
                            {
                            case "dm_sysobject":
                                superType = siteContentTypes["Document"];
                                // skipInherited = false;
                                patchFields     = true;
                                versionableType = true;
                                break;

                            case "dm_folder":
                                superType = siteContentTypes["Folder"];
                                // skipInherited = false;
                                patchFields   = true;
                                containerType = true;
                                break;

                            default:
                                // This isn't a type we're intereseted in, so we skip it
                                continue;
                            }
                        }

                        Log.Info(string.Format("Creating content type {0} (descended from [{1}]])", typeName, superType.Name));
                        ContentTypeCreationInformation ctInfo = new ContentTypeCreationInformation();
                        ctInfo.Description       = string.Format("Documentum Type {0}", typeName);
                        ctInfo.Name              = typeName;
                        ctInfo.ParentContentType = (superType != null ? superType.Type : null);
                        ctInfo.Group             = this.TypeGroup;

                        ContentType contentTypeObj = contentTypeCollection.Add(ctInfo);
                        clientContext.Load(contentTypeObj, t => t.Id);
                        session.ExecuteQuery();

                        finalType = new ImportedContentType(this, contentTypeObj, superType.Id);
                        siteContentTypes[typeName] = finalType;
                        contentTypesById[finalType.Id.StringValue] = finalType;
                    }

                    // Now we link the type to its fields, as needed
                    int updateCount = 0;

                    XElement attributeContainer = type.Element(ns + "attributes");
                    if (patchFields)
                    {
                        XElement versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "32");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "version");
                        versionAtt.SetAttributeValue("name", "cmf:version");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "400");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "title");
                        versionAtt.SetAttributeValue("name", "cmis:description");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "128");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "author_name");
                        versionAtt.SetAttributeValue("name", "shpt:authorName");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "0");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "author");
                        versionAtt.SetAttributeValue("name", "shpt:author");
                        versionAtt.SetAttributeValue("dataType", "USER");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "128");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "editor_name");
                        versionAtt.SetAttributeValue("name", "shpt:editorName");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "0");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "editor");
                        versionAtt.SetAttributeValue("name", "shpt:editor");
                        versionAtt.SetAttributeValue("dataType", "USER");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "16");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "object_id");
                        versionAtt.SetAttributeValue("name", "cmis:objectId");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "256");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "location");
                        versionAtt.SetAttributeValue("name", "cmf:location");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "256");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "path");
                        versionAtt.SetAttributeValue("name", "cmis:path");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "256");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "name");
                        versionAtt.SetAttributeValue("name", "cmis:name");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "1024");
                        versionAtt.SetAttributeValue("repeating", "true");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "keywords");
                        versionAtt.SetAttributeValue("name", "dctm:keywords");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        versionAtt = new XElement(ns + "attribute");
                        versionAtt.SetAttributeValue("length", "16");
                        versionAtt.SetAttributeValue("repeating", "false");
                        versionAtt.SetAttributeValue("inherited", "false");
                        versionAtt.SetAttributeValue("sourceName", "acl_id");
                        versionAtt.SetAttributeValue("name", "dctm:acl_id");
                        versionAtt.SetAttributeValue("dataType", "STRING");
                        attributeContainer.AddFirst(versionAtt);

                        if (versionableType)
                        {
                            versionAtt = new XElement(ns + "attribute");
                            versionAtt.SetAttributeValue("length", "16");
                            versionAtt.SetAttributeValue("repeating", "false");
                            versionAtt.SetAttributeValue("inherited", "false");
                            versionAtt.SetAttributeValue("sourceName", "history_id");
                            versionAtt.SetAttributeValue("name", "cmis:versionSeriesId");
                            versionAtt.SetAttributeValue("dataType", "STRING");
                            attributeContainer.AddFirst(versionAtt);

                            versionAtt = new XElement(ns + "attribute");
                            versionAtt.SetAttributeValue("length", "16");
                            versionAtt.SetAttributeValue("repeating", "false");
                            versionAtt.SetAttributeValue("inherited", "false");
                            versionAtt.SetAttributeValue("sourceName", "antecedent_id");
                            versionAtt.SetAttributeValue("name", "cmf:version_antecedent_id");
                            versionAtt.SetAttributeValue("dataType", "STRING");
                            attributeContainer.AddFirst(versionAtt);

                            versionAtt = new XElement(ns + "attribute");
                            versionAtt.SetAttributeValue("length", "0");
                            versionAtt.SetAttributeValue("repeating", "false");
                            versionAtt.SetAttributeValue("inherited", "false");
                            versionAtt.SetAttributeValue("sourceName", "current");
                            versionAtt.SetAttributeValue("name", "cmis:isLatestVersion");
                            versionAtt.SetAttributeValue("dataType", "BOOLEAN");
                            attributeContainer.AddFirst(versionAtt);
                        }
                    }

                    foreach (XElement att in attributeContainer.Elements(ns + "attribute"))
                    {
                        // The attribute is either not inherited or its inheritance is ignored, so add it to the content type's declaration
                        string attName       = att.Attribute("name").Value;
                        string attSourceName = att.Attribute("sourceName").Value;
                        string finalName     = string.Format("dctm_{0}", attSourceName);
                        // Special case for folder attributes inherited from dm_sysobject
                        bool inherited = XmlConvert.ToBoolean(att.Attribute("inherited").Value) && (typeName != "dm_folder");
                        bool repeating = XmlConvert.ToBoolean(att.Attribute("repeating").Value);

                        // If this is an inherited attribute, we won't add it because we're not interested in it
                        if (inherited && skipInherited)
                        {
                            continue;
                        }

                        ImportedContentTypeField finalField = null;
                        if (linkNames.Contains(finalName) || (inherited && skipInherited))
                        {
                            // Existing or inherited link...
                            finalField = new ImportedContentTypeField(existingFields[finalName], attName, finalName, attSourceName, repeating);
                        }
                        else
                        {
                            FieldLinkCreationInformation fieldLink = new FieldLinkCreationInformation();
                            if (existingFields.ContainsKey(finalName))
                            {
                                finalField      = new ImportedContentTypeField(existingFields[finalName], attName, finalName, attSourceName, repeating);
                                fieldLink.Field = finalField.Field;
                            }
                            else
                            {
                                Log.Info(string.Format("Creating field {0} (first declared by {1})", finalName, typeName));
                                FieldType attType = Tools.DecodeFieldType(att.Attribute("dataType").Value);
                                if (repeating)
                                {
                                    // Default repeating fields to strings, since they'll be concatenated
                                    attType = FieldType.Note;
                                }
                                int length = XmlConvert.ToInt32(att.Attribute("length").Value);
                                if (length > 255)
                                {
                                    attType = FieldType.Note;
                                }
                                Guid guid = Guid.NewGuid();


                                string fieldXml = string.Format("<Field DisplayName='{0}' Name='{1}' ID='{2}' Group='{3}' Type='{4}' />", finalName, finalName, guid.ToString(), this.FieldGroup, attType);
                                fieldLink.Field = clientContext.Web.Fields.AddFieldAsXml(fieldXml, false, AddFieldOptions.AddFieldInternalNameHint);
                                clientContext.Load(fieldLink.Field, f => f.Id, f => f.FieldTypeKind, f => f.StaticName, f => f.Group);
                                existingFields[finalName] = fieldLink.Field;
                                finalField = new ImportedContentTypeField(existingFields[finalName], attName, finalName, attSourceName, repeating);
                            }
                            finalType.Type.FieldLinks.Add(fieldLink);
                            linkNames.Add(finalName);
                            updateCount++;
                        }
                        finalType.AddField(finalField);
                    }
                    if (updateCount > 0)
                    {
                        finalType.Type.Update(true);
                        session.ExecuteQuery();
                    }
                    newTypes.Add(typeName);
                }

                // Now, make sure this type exists in the document library
                List <ContentType> newContentTypes = new List <ContentType>();
                foreach (string typeName in newTypes)
                {
                    if (!documentLibraryTypes.Contains(typeName))
                    {
                        ContentType ct = documentLibrary.ContentTypes.AddExistingContentType(siteContentTypes[typeName].Type);
                        newContentTypes.Add(ct);
                        clientContext.Load(ct);
                        clientContext.Load(ct, c => c.Parent);
                    }
                }
                if (newContentTypes.Count > 0)
                {
                    session.ExecuteQuery();
                    foreach (ContentType ct in newContentTypes)
                    {
                        ImportedContentType newCt = new ImportedContentType(this, ct);
                        contentTypesById[ct.Id.StringValue] = newCt;
                        libraryContentTypes[ct.Name]        = newCt;
                    }
                }

                this.SiteContentTypes    = siteContentTypes;
                this.LibraryContentTypes = libraryContentTypes;
                this.ContentTypesById    = contentTypesById;
            }
        }