Social Icons

twitter google plus linkedin rss feed

Pages

9.7.15

Is JavaScript The Emperor's New Clothes?

Script (computing), a small non-compiled program written for a scripting language or command interpreter.

If JavaScript has Script in its name, what made you think it would be a suitable language to base the whole web and half of the mobile software on it?

If JavaScript: The good parts is a best seller and it's only a 150 pages manual (50 are appendixes and only 2 are about beautiful features and I am pretty sure even Mr Crockford found it excruciating to write so many)

If you are required to migrate from one version to another every now and then because the software provider will only support your bits for the next five years, What makes you think it's a good idea to download and install in the core of your software a random minified js file that you can't read and you don't know where it comes from and, of course, was forgotten by its developer five days after he published it?

Will I work with it?

Of course I will, in fact I have been doing it for ages, just not at this level. If you can do anything with a Turing machine why not embracing a script language for making applications of thousands of lines? That's what the brave would call a challenge.

I have been creating classes and constructors and inheritance and it all can be done in JS but I am pretty sure translating ancient OO structures and patterns to JS is not the right way, probably that's my problem. Instead of embracing the new language I am trying to translate my old jokes. That will probably change with the time as I learn the new ones.

The fact that we have no other option plays a role here too. If you are working for the web you are free to use JavaScript or not to work for the web Ã  la Apple.

Do I understand its advantages?

The learning curve is great as the basics are simpler. No types no safety net. No need of an IDE no great IDEs either, if you are used to Visual Studio you'll love loosing all the features you are used to. It can be executed everywhere if they have the right browser and that's not the case in many big companies. And a great community of fans Ã  la Apple.

Do I like JavaScript?

Do I like a piece of technology that, if we are lucky, will solve 5 years in the future all the problems we solved with Silverlight eight years ago?

I probably will when both me and the technology are mature enough.

Of course I do, I want to be cool...


I learnt to love WPF but it didn't take this long.

No comments:

Post a Comment

11.6.15

Migrating SharePoint Users to a New Domain

I have been dreading this type of migration for years... so many years that I already had planned a couple of ways of solving the issue. It finally happened.

Scenario:

Someone decides we need to change the farm from one environment to a new one completely different with a new AD and in a new city.

Well, let's get to it. We have created a new SharePoint farm in the new environment and we have backed up and restored the content databases. We have manually changed the admin of the site collection to the new admin in the new AD in the new farm and we can access the site and see the data. Fantastic.

Fantastic?

The users in the list items are the users from the old farm. And we have several lists with a lot of user fields. And some of our lists have tens or hundreds of thousands of rows. Changing them manually is not an option.

First idea: Go refined and try stsadm -o migrateuser:

Ohh so easy... we change the login name of the user to something else and we are good because the user IDs are still the same... NO.

This is a new domain and we don't have access to the old domain users so the migrateuser parameter throws a nice User not found error.

Second idea: Go berserk and change the strings in the list items

And that worked. Oh the beauty of a simple idea. The process is pure brute force... beautiful in its barbarity... If you have read up to here you are probably desperate for a solution.

Step One:
Get all of the users from the old farm in an XML file or something really high tech (a csv could work too).

static void Main(string[] args)
{
    using (SPSite site = new SPSite(args[0]))
    {
        using (SPWeb web = site.OpenWeb())
        {
            XElement users = new XElement("Users");

            foreach (SPUser user in web.SiteUsers)
            {
                XElement xmlUser = new XElement("User");
                xmlUser.Add(new XAttribute("Name", user.Name));
                xmlUser.Add(new XAttribute("LoginName", user.LoginName));
                xmlUser.Add(new XAttribute("ID", user.ID));

                users.Add(xmlUser);
            }

            users.Save("SiteUsers.xml");
        }
   
    }

    Console.WriteLine("\nProcess finished...");
    Console.ReadLine();
}


Step Two:
Make sure all the users you need are in the new AD. As you have a list in XML you can pass it to someone with privileges in the AD.

Step Three:
Ensure the users in SharePoint, add them to a group with reading permissions and then iterate through all the items in the list changing the users from the old domain to the users in the new one.

static void Main(string[] args)
{
    XElement users = XElement.Load("SiteUsers.xml");
    string newDomain = "XXXXXXXX";

    string ListName = string.Empty;
    if (args.Length == 2) ListName = "Stratex Framework";
    else ListName = args[2];

    //Args are SiteUrl VisitorsGroup ListName
    using (SPSite site = new SPSite(args[0]))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SPList listToUpdate = web.Lists[ListName];

            Dictionary<string, SPUser> NewUsers = new Dictionary<string, SPUser>();

            foreach (XElement user in users.Descendants("User"))
            {
                string LoginName = FixDomain(user.Attribute("LoginName").Value, newDomain);
                SPUser spUser = null;
                try
                {
                    //We try to ensure all the users from the XML file. We'll probably need them
                    spUser = web.EnsureUser(LoginName);
                }
                catch
                { Logger.WriteLine("The user {0} could not be found.", LoginName); }

                if (spUser != null)
                {
                    SPGroup viewers = web.Groups[args[1]];

                    viewers.AddUser(spUser);
                    //Finally we add them to a group with read permissions
                    //We can worry about restricting this further after the migration

                    if (!NewUsers.ContainsKey(LoginName)) NewUsers.Add(user.Attribute("ID").Value, spUser);
                }
            }

            web.Update();


            UpdateUsersInList(listToUpdate, NewUsers);
        }

    }

    Logger.WriteLine("\nProcess finished...");
    Console.ReadLine();
}

private static void UpdateUsersInList(SPList list, Dictionary<string, SPUser> NewUsers)
{
    int itemsInList = list.ItemCount;
    Logger.WriteLine("Updating users at {0}. {1} items.", list.Title, itemsInList.ToString());

    SPQuery qry = new SPQuery();
    qry.ViewAttributes = "Scope=\"RecursiveAll\"";
    SPListItemCollection allItems = list.GetItems(qry);
    int count = 0;

    UpdateCount(count++, itemsInList);

    foreach (SPListItem item in allItems)
    {
        try
        {
            bool changed = false;
            SPFieldCollection allFields;
            //If the item has content type it has usualy less fields
            if (item.ContentType == null)
                allFields = item.Fields;
            else
                allFields = item.ContentType.Fields;

            foreach (SPField field in allFields)
            {
                if (field is SPFieldUser)
                    changed = ChangeUserToNewDomain(item, field, NewUsers) || changed;
            }

            changed = ChangeUserToNewDomain(item, item.Fields.GetFieldByInternalName("Author"), NewUsers) || changed;
            changed = ChangeUserToNewDomain(item, item.Fields.GetFieldByInternalName("Editor"), NewUsers) || changed;

            if (changed) item.SystemUpdate(false); //if the item has not been changed we won't update it to save time
        }
        catch (Exception ex) { Logger.WriteLine("Failed to update item {0}. Exception {1}", item.Title, ex.Message); }

        UpdateCount(count++, itemsInList);
    }

    UpdateCount(count++, 0);
}

private static bool ChangeUserToNewDomain(SPListItem item, SPField field, Dictionary<string, SPUser> NewUsers)
{
    bool changed = false;
    string fieldContent = item[field.InternalName] == null ? null : item[field.InternalName].ToString();

    if (string.IsNullOrEmpty(fieldContent)) return false;

    List<string> oldUserIds = GetUserIDs(fieldContent.Split(new string[] { ";#" }, StringSplitOptions.RemoveEmptyEntries));

    if (oldUserIds.Count == 1)
    {   //The field has only one user in it
        SPFieldUserValue foundUser = FindUser(NewUsers, oldUserIds[0]);

        if (foundUser != null)
        {
            item[field.InternalName] = foundUser;
            changed = true;
        }
    }
    else if (oldUserIds.Count > 1)
    {   //The field has several users in it
        SPFieldUserValueCollection usersInField = new SPFieldUserValueCollection();
        foreach (string oldUser in oldUserIds)
        {
            SPFieldUserValue foundUser = FindUser(NewUsers, oldUser);

            if (foundUser != null)
                usersInField.Add(foundUser);
        }

        if (usersInField.Count > 0)
        {
            item[field.InternalName] = usersInField;
            changed = true;
        }
    }
            
            
    return changed;
}

private static List<string> GetUserIDs(string[] UserTokens)
{   //We do not care about the login name. The ID is gold
    List<string> result = new List<string>();

    if (UserTokens.Length > 0)
    {
        for (int i = 0; i < UserTokens.Length; i++)
        {
            int id;

            if (i % 2 == 0 && int.TryParse(UserTokens[i], out id))
                result.Add(id.ToString());
        }
    }

    return result;
}

private static SPFieldUserValue FindUser(Dictionary<string, SPUser> NewUsers, string oldUser)
{
    SPUser foundUser = null;

    if (NewUsers.ContainsKey(oldUser)) foundUser = NewUsers[oldUser];
    else
    {
        //If we can't find the ID of the user we will still try with the login or even with the Display Name
        foreach (SPUser newUser in NewUsers.Values)
        {
            if (newUser.Name == oldUser || newUser.LoginName == oldUser) { foundUser = newUser; break; }
        }
    }

    if (foundUser != null)
        return new SPFieldUserValue(foundUser.ParentWeb, foundUser.ID, foundUser.Name);
    else
        return null;
}

private static string FixDomain(string loginName, string newDomain)
{
    //Here we change the users from XXXXX\\User to YYYYY\\User
    //The source domain was claim based
    if (loginName.Contains("|")) loginName = loginName.Split('|')[1];

    string[] tokens = loginName.Split('\\');

    tokens[0] = newDomain;

    return string.Join("\\", tokens);
}

private static void UpdateCount(int currentItem, int itemsInList)
{
    int percentage;

    if (currentItem == 0) percentage = 0;
    else if (itemsInList == 0) percentage = 100;
    else
    {
        //We will only change the value every 10 times to make the process faster.
        if (currentItem % 10 != 0) return;
        percentage = currentItem * 100 / itemsInList;
    }
    Console.Write("\r");
    if (percentage >= 0 && percentage < 10)
        Console.Write("  ");
    else if (percentage >= 10 && percentage < 100)
        Console.Write(" ");

    Console.Write("{0}%", percentage);
}

This is a first prototype that has worked as expected but it's not fully tested (by far) if you need it you can use it as a base to develop your own tool.

The one who possesses the strings has the power.

No comments:

Post a Comment