Learning AI – Part 1: Genetic Algorithms Intro

Hi,

This is my first post in a series of Artificial Intelligence posts on AI learning. I got interested in the subject a few weeks ago when I started to work on some ideas for a game prototype I had in my mind. I was reading a book on game AI and turned the pages to a section of the book about AI learning It was fascinating :)! Anyways I remembered one of my older project where I played around with physics and nature I decided to back to it and the book that inspired me to nature and how it works.

So in this first post I’ll show you guys some sample C# code for a simple Genetic Algorithm that solves a text puzzle. On in other words I am using a GA algorithm so find the fastest solution to get the same text output from the algorithm that is input to the algorithm.

To start with if you are not familiar with Genetic Algorithms this source is a good place to start. My sample code at the moment is based on this page. This is where I will continue from later in the future parts of these upcoming blog posts on learning AI: http://natureofcode.com/book/chapter-9-the-evolution-of-code/

Logic and code

So lets start, ouh and remember I am just starting to learn this so double check the info and test for yourself :):

To put it in simple words GA algorithm is about inheriting data from generation to generation where the best candidate from the desired goal is chosen until the desired outcome generations later is achieved. Now this is a simplification of what is going on and there are more variables to take into consideration. I will keep things as simple as I can and hope that you refer to the link above or to more technical sources for more in-depth knowledge.

To start with a GA algorithm needs two things:

  1. genotype => Digital information that is passed down from generation to generation
  2. phenotype => this is the expression of data, how the data is going to be represented. This could be data that tells a system where an object is on the screen.

In this first part where the code sample is trying to solve a text problem the genotype and phenotype are one and the same called: DNA

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AIEngine.DataStructures
{
 public class DNA : IDNA
 {
 public List<Char> Genes { get; set; }
 public float Fitness { get; set; }
 public String Target { get; set; }

 public DNA()
 {
 this.Genes = new List<char>();
 }

 public DNA(int genesCount, String target)
 {
 this.InitializeGenes(genesCount);
 this.Target = target;
 }

 public void InitializeGenes(int genesCount)
 {
 this.Genes = new List<char>();
 if (genesCount < 0)
 throw new Exception("The genesCount must be larger than 0");
 else if (genesCount == 0)
 return;

 int index = 0;
 do
 {
 this.Genes.Add((char)RandomProvider.RND.Next(32, 128));
 index++;
 } while (index < genesCount);
 }

 public void EvalutateFitness()
 {
 int score = 0;
 for (int i = 0; i < this.Genes.Count; i++) 
 {

 if (this.Target[i] == this.Genes[i])
 score++;
 }

 this.Fitness = (float)score/this.Target.Length;
 }

 public IDNA Crossover(IDNA partner)
 {
 DNA child = new DNA(0, this.Target);
 int midpoint = RandomProvider.RND.Next(this.Genes.Count);

 for(int x = 0; x < this.Genes.Count; ++x) { if (x > midpoint)
 child.Genes.Insert(x,this.Genes[x]);
 else
 child.Genes.Insert(x, partner.Genes[x]);
 }

 return child;
 }

 public void Mutate(float mutationRate)
 {
 for (int i = 0; i < this.Genes.Count; i++) {
 float rndValue = (float)RandomProvider.GetRandomNumber(RandomProvider.RND, 0, 1);
 if (rndValue < mutationRate)
 {
 this.Genes[i] = (char)RandomProvider.RND.Next(32, 128);
 }
 }
 }

 public override string ToString()
 {
 return new StringBuilder().Append(this.Genes.ToArray()).ToString();
 }
 }
}

What this class will do is generate a random number of character between a certain ASCII range. It will also evaluate a fitness for a DNA object to be used when deciding if the DNA will reproduce. Also to keep variation and increase the changes of a faster solution solving a crossover between two DNA objects are made. Also as a last measure to ensure that the best possible solution is achieved mutation to the DNA is introduced where a random character at random times is replaced in a DNA sequence of characters.

Next we will look at the population control class :D

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AIEngine.DataStructures;

namespace AIEngine
{
 public class Population
 {
 ///
<summary>
 /// Change this value to alter how fast a problem is solved
 /// </summary>

 private int populationCount = 1000;
 private String target = "TO BE OR NOT TO BE";
 private List<DNA> population;
 private List<DNA> matingPool;
 private int perfectScore;
 private Boolean finished;
 private int generations;

 ///
<summary>
 /// Change this value to alter how fast a problem is solved
 /// </summary>

 private float mutationRate = 0.01F; // This value of 0.01 with a population count of 1000 seems to be generating the fastest result with monte carlo mating

 ///
<summary>
 /// 
 /// </summary>

 /// <param name="target"></param>
 /// <param name="mutationRate"></param>
 /// <param name="populationCount"></param>
 public Population(String target, float mutationRate, int populationCount)
 {
 this.target = target;
 this.mutationRate = mutationRate;
 this.populationCount = populationCount;

 population = new List<DNA>();
 matingPool = new List<DNA>();
 for (int x = 0; x < this.populationCount; x++)
 {
 population.Add(new DNA(target.Length, target.ToUpper()));
 }

 this.CalculateFitness();

 this.finished = false;
 this.generations = 0;
 this.perfectScore = 1;
 }

 ///
<summary>
 /// In each iteration we calculate the fitness of each DNA sequence to be used later in the algorithm logic
 /// </summary>

 public void CalculateFitness()
 {
 for (int x = 0; x < population.Count; x++)
 {
 population[x].EvalutateFitness();
 }
 }

 ///
<summary>
 /// Here the algorithm implements a selection method for chosing the best DNA sequences from the population.
 /// </summary>

 public void NaturalSelection()
 {
 bool duplicateFound = true;
 matingPool.Clear();

 for (int x = 0; x < population.Count; x++) { //while (duplicateFound) { // Monte carlo method(Faster than the below commented approach) int a = RandomProvider.RND.Next(population.Count); int b = RandomProvider.RND.Next(population.Count); int populationIndexChosen = -1; if (population[a].Fitness > population[b].Fitness)
 {
 populationIndexChosen = a;
 }
 else
 {
 populationIndexChosen = b;
 }

 matingPool.Add(population[populationIndexChosen]);

 //if (!matingPool.Exists( o => o.ToString() == population[populationIndexChosen].ToString()))
 //{
 // matingPool.Add(population[populationIndexChosen]);
 // duplicateFound = false;
 //}

 //int n = (int)(population[x].Fitness * 100);
 //for (int j = 0; j < n; j++)
 //{

 // matingPool.Add(population[x]);
 //}
 }
 }
 }

 ///
<summary>
 /// Next we will generate a new population based on algorithmic logic of crossover between two random DNA sequences and adding some mutation into it.
 /// </summary>

 public void Generate()
 {
 for (int i = 0; i < population.Count; i++)
 {
 {
 int a = RandomProvider.RND.Next(matingPool.Count);
 int b = RandomProvider.RND.Next(matingPool.Count);
 // TODO: Avoid duplicates
 DNA partnerA = matingPool[a];
 DNA partnerB = matingPool[b];

 DNA child = (DNA)partnerA.Crossover(partnerB);
 child.Mutate(mutationRate);
 child.EvalutateFitness();
 population[i] = child;
 }
 }
 this.generations++;
 }

 public String GetBest()
 {
 float worldrecord = 0.0F;
 int index = 0;
 for (int i = 0; i < population.Count; i++) { if (population[i].Fitness > worldrecord)
 {
 index = i;
 worldrecord = population[i].Fitness;
 }
 }

 if (worldrecord == perfectScore) finished = true;
 return population[index].ToString();
 }

 public Boolean Finished()
 {
 return this.finished;
 }

 public int GetGenerations()
 {
 return generations;
 }

 // Compute average fitness for the population
 public float GetAverageFitness()
 {
 float total = 0;
 for (int i = 0; i < population.Count; i++) { total += population[i].Fitness; } return total / (population.Count); } public String AllPhrases() { StringBuilder everything = new StringBuilder(); for (int i = population.Count -1; i > population.Count - 11; i--)
 {
 everything.AppendLine(population[i].ToString());
 }
 return everything.ToString();
 }
 }
}

The Population classes main function is to hold the main population of DNA sequences and the mating pool used to generate the next generation of population (or to replace the current generation of population with the next generation). Also some statistical data can be retrieved from the class itself along with the information when the problem has been solved. In this case when the output is the same as the input.

Next is actual main program which will run the population to solve a problem.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AIEngine.DataStructures;
using AIEngine;

namespace GeneticAlgoritmTextTest
{
 class Program
 {
 ///
<summary>
 /// Change this value to alter how fast a problem is solved
 /// </summary>

 public static int PopulationCount = 1000;
 public static String target = "TO BE OR NOT TO BE";

 ///
<summary>
 /// Change this value to alter how fast a problem is solved
 /// </summary>

 public static float mutationRate = 0.01F;
 static void Main(string[] args)
 {
 bool exit = false;

 // Create the population which is responsible for solving the problem.
 Population population = new Population(target, mutationRate, PopulationCount);
 while (!exit)
 {
 // In each iteration we calculate the fitness of each DNA sequence to be used later in the algorithm logic
 population.CalculateFitness();
 // Here the algorithm implements a selection method for chosing the best DNA sequences from the population.
 population.NaturalSelection();
 // Next we will generate a new population based on algorithmic logic of crossover between two random DNA sequences and adding some mutation into it.
 population.Generate();
 

 Console.WriteLine();
 Console.WriteLine(population.AllPhrases());
 Console.WriteLine("Cycle average fitness: " + population.GetAverageFitness());
 Console.WriteLine("Total generations: " + population.GetGenerations());
 Console.WriteLine("Best fitness in cycle: " + population.GetBest());

 // And before we go to the next iteration we check to see if the text puzzle has been solved.
 exit = population.Finished();
 }
 Console.ReadLine();
 }
 }
}

The code above simple creates a population which will try to solve the a text puzzle. A fitness is calculated for each member of the population. A selection method is applied for the population and a mutation is introduced at random population members.

The end result should be something like this:

GABasicDNAStart

GABasicDNAEnd

There are two images above. The first one shows the very first populations in the algorithm. The last image show the very last population generations up until the very last one which solved the problem.

Optimization

There are two things to do if you want to optimize the algorithm in this example:

  1. Changing the mutation rate and population count variables.
    1. By playing around with these values you can find out the most “optimal” values to solve your desired problem. You can do this manually or you can write a piece of code which will do this for you based on the telemetry from the algorithm.
      1. Notice that having to a too large mutation rate will make the algorithm unsolvable and like wise having a too small or way too large population count will cause your algorithm to take longer or a very long time.
  2. The fitness function logic
    1. Here I will simply quote Daniel Shiffman from The Nature of code: “If you cannot define your problem’s goals and evaluate numerically how well those goals have been achieved, then you will not have successful evolution in your simulation.” So defining a good fitness function will go a long way towards creating a better result. Also each problem will most likely have a very unique fitness function. Similarities can occur(Still learning :) ).

The source code for this project you will find here: https://github.com/lionadi/NatureOfCodePlayground/tree/master/NatureOfCodeCSharp%20AI%20Project

Also as you can see the code is a work in progress, not pretty and not ready :). So changes in the github versions are due to come in the future.

SharePoint 2013 EMail Alerts with wrong URLs under extended zones

I had to make a fix to the problem described in the title above. Basically the issue is that if you have an extended zone and you are initiating an alert from you SharePoint environment in the E-Mail.

There is the following solution which works perfectly:

https://timpanariuovidiu.wordpress.com/2014/06/23/sharepoint-2013-alerts/

http://blogs.msdn.com/b/sharepointdeveloperdocs/archive/2007/12/14/how-to-customizing-alert-emails-using-ialertnotificationhandler.aspx

The sample above works just fine. I did had to make an addition to the code because it was not taking into consideration the headers and the wrong URL there. Here is my version. It is rather similar:



public class CustomAlertHandler : IAlertNotifyHandler
 {
 public bool OnNotification(SPAlertHandlerParams ahp)
 {
 try
 {
 using (SPSite site = new SPSite(ahp.siteUrl + ahp.webUrl))
 {
 using (SPWeb spWeb = site.OpenWeb())
 {
 SPAlert a = ahp.a;

 if (a.Properties.ContainsKey("siteurl"))
 {
 string siteNewURL = a.Properties["siteurl"];

 string siteOldURL = ahp.siteUrl;

 ahp.body = ahp.body.Replace(siteOldURL, siteNewURL);

 ahp.siteUrl = siteNewURL;
// My Addition for the header URL
 if (ahp.headers["x-sharing-config-url"] != null && !String.IsNullOrEmpty(siteNewURL) && !String.IsNullOrEmpty(siteOldURL))
 ahp.headers["x-sharing-config-url"] = ahp.headers["x-sharing-config-url"].ToLower().Replace(System.Web.HttpUtility.UrlEncode(siteOldURL).Replace(".", "%2e"), System.Web.HttpUtility.UrlEncode(siteNewURL).Replace(".", "%2E"));
 }

 string to = ahp.headers["to"].ToString();

 string subject = ahp.headers["subject"].ToString();

 SPUtility.SendEmail(spWeb, ahp.headers, ahp.body);
 }
 }
 return true;
 }
 catch (Exception ex)
 {
 return false;
 }
 }
 }


Additionally to the code above which needs to be deployed from a solution package you also need to create a custom alert template based on the SharePoint alerttemplates.xml in C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\XML.

In the XML within the properties tags you needs to add your assemble reference:

<NotificationHandlerAssembly>AlertHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d59ecf2a3bd66904</NotificationHandlerAssembly>

<NotificationHandlerClassName>AlertHandler.Class1</NotificationHandlerClassName>

<NotificationHandlerProperties></NotificationHandlerProperties>

Then do the following steps:

Run the following command next:
stsadm -o updatealerttemplates -filename “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\XML\customalerttemplates.xml” -url “Your site URL”

Run iisreset on each server of the farm.

Restart the SharePoint timer services on each server in the farm.

Check out the links above for more info on how to create a custom alert.

SharePoint Online – PowerShell, CSOM and Tools

Here are some links on how to access and modify your SharePoint Online sites and data. These might come in handy to someone whom is starting with SharePoint Online:

SharePoint Online Management Shel

Index of Windows PowerShell for SharePoint Online cmdlets

Windows PowerShell for SharePoint Online reference

SharePoint Client Browser for SharePoint Online and SharePoint on-premise

SharePoint Online Client Components SDK

Check out this great link for more info on how to use these tools:

http://blogs.technet.com/cfs-file.ashx/__key/telligent-evolution-components-attachments/01-9981-00-00-03-63-30-65/sharepoint-online-administration-with-powershell.pdf

Tiled2Unity – Using normal maps for your tiled projects

I needed to use normal maps to my Tiled project with Tiled2Unity and this was not support by default. So I did some script changes to make this happen. Below is the instruction how to use the new code changes.

How to use Ti led with Tiled2Unity to attach Normal Maps to your Tiled project:

  • Add a new map property named “UseNormalMap” and assign the value 1 to it. This will indicate to the script in unity that a normal map is to searched and applied to the project once exported from Tiled2Unity.

tiled2Unitynormalmapproperties

tiled2unityNormalMapProperty

  • Next add you normal map texture to the Tiled project, make sure that the normal map texture file has the following characters just before the file extension: _n, example: hyptosis_til-art-batch-2_n.png. This will tell the importer code which texture file is the normal map file. So it is the same file name as the original texture file but with the _n characters. Then add a new layer and name it “NormalMapLayer”. IMPORTANT: Add some tiles from the normal map texture to the layer. This is so that the normal map texture is exported to Unity. If this is not done the normal map texture is not exported by the tool Tiled2Unity.tiled2UnityNormalMapLayer
  • After this simply do what you would normally do to export your tiled project to Unity using Tiled2Unity. This of course implies that you have the code changes which support this new feature for normal maps.
  • NOTICE: There is a new shader called TextureTintSnapNormal which is used for normal mapping.

My changes to Tiled2Unity Scripts for Unity: https://github.com/lionadi/Tiled2Unity/tree/master/unity/Tiled2Unity/Scripts/Editor

And the custom shader: https://github.com/lionadi/Tiled2Unity/tree/master/unity/Tiled2Unity/Shaders

Original project: http://www.seanba.com/tiled2unity

Tiled project: http://www.mapeditor.org/

 

Normal tools for 2D art:

https://www.codeandweb.com/spriteilluminator

http://www.2deegameart.com/p/sprite-dlight.html

http://www.snakehillgames.com/spritelamp/

http://crazybump.com/

https://github.com/spritebuilder/NormalMixer

O365 CSOM – Client Side Object Model examples

Here are some examples on how to use CSOM with O365:

This is Microsofts’ link on the matter for more examples:

https://msdn.microsoft.com/en-us/library/ff798388.aspx

 

Remove SiteCollection


 var tenantAdminUri = new Uri(tenantAdminUrl);
 string realm = TokenHelper.GetRealmFromTargetUrl(tenantAdminUri);
 var token = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, tenantAdminUri.Authority, realm).AccessToken;
 using (var tenantContext = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), token))
 {
 // Set the time out as high as possible
 tenantContext.RequestTimeout = int.MaxValue;

 var tenant = new Tenant(tenantContext);

 //start the SPO operation to create the site
 SpoOperation op = tenant.RemoveSite(webUrl);
 tenantContext.Load(op, i => i.IsComplete);
 tenantContext.RequestTimeout = int.MaxValue;
 tenantContext.ExecuteQuery();

 while (!op.IsComplete)
 {
 Console.WriteLine("Waiting 30 seconds site to be provisioned");
 //wait 30seconds and try again
 System.Threading.Thread.Sleep(30000);
 op.RefreshLoad();
 tenantContext.ExecuteQuery();
 }
 }

 Updating a list item

</pre>
List list = ctx.Web.Lists.GetByTitle(ConfigurationManager.AppSettings["SiteCollectionRequests_List"]);
ListItem listItem = list.GetItemById(id);
ctx.Load(listItem);
ctx.ExecuteQuery();

statusMessage += "Your message";
listItem["StatusMessage"] = statusMessage;
listItem.Update();
list.Update();
ctx.Web.Update();
ctx.ExecuteQuery();
<pre>

Adding a new item to a list


List list = ctx.Web.Lists.GetByTitle(ConfigurationManager.AppSettings["SiteCollectionRequests_LogList"]);

 ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
 ListItem newItem = list.AddItem(itemCreateInfo);
 newItem["Title"] = title;
 newItem["url"] = workspaceURL;

 XmlSerializer xs = new XmlSerializer(typeof(WorkspacePrivileges));
 StringWriter sww = new StringWriter();
 XmlWriter writer = XmlWriter.Create(sww);
 xs.Serialize(writer, workspacePrivileges);
 String aaa = sww.ToString();

 newItem["my text field"] = sww.ToString();
 newItem.Update();
 ctx.ExecuteQuery();

Change Workspace Privileges To Read Only


Uri siteUrl = new Uri(workspaceURL);
 string realm = TokenHelper.GetRealmFromTargetUrl(siteUrl);
 var token = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUrl.Authority, realm).AccessToken;
 using (var ctxWorkspace = TokenHelper.GetClientContextWithAccessToken(siteUrl.ToString(), token))
 {
 Web currentWeb = ctxWorkspace.Web;
 ctxWorkspace.Load(currentWeb);

 UserCollection webUsers = currentWeb.SiteUsers;
 ctxWorkspace.Load(webUsers);
 ctxWorkspace.ExecuteQuery();
 List<String> usersToAddReadOnlyRights = new List<string>();

 foreach(User user in webUsers)
 {
 if (user.LoginName.ToLowerInvariant().Contains("membership"))
 {
 if (!user.IsSiteAdmin)
 {
 webUsers.RemoveById(user.Id);
 usersToAddReadOnlyRights.Add(user.LoginName);
 }
 }
 }
 currentWeb.Update();
 ctxWorkspace.ExecuteQuery();

 Group visitorGroup = ctxWorkspace.Web.SiteGroups.GetByName(ctxWorkspace.Web.Title + ConfigurationManager.AppSettings["WorkspaceVisitors"]);
 ctxWorkspace.Load(currentWeb);
 ctxWorkspace.Load(visitorGroup);
 ctxWorkspace.Load(webUsers);
 ctxWorkspace.ExecuteQuery();
 foreach (String user in usersToAddReadOnlyRights)
 {
 String[] userLoginNameArray = user.Split('|');
 if(userLoginNameArray != null && userLoginNameArray.Length > 1)
 {
 UserCreationInformation userInfo = new UserCreationInformation();
 userInfo.LoginName = userLoginNameArray[2];
 currentWeb.AddUserToGroup(visitorGroup, userLoginNameArray[2]);

 }
 }
 currentWeb.Update();
 ctxWorkspace.ExecuteQuery();

Remove Workspace users privileges And deny Search Indexing on lists


Uri siteUrl = new Uri(workspaceURL);
 string realm = TokenHelper.GetRealmFromTargetUrl(siteUrl);
 var token = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUrl.Authority, realm).AccessToken;
 using (var ctxWorkspace = TokenHelper.GetClientContextWithAccessToken(siteUrl.ToString(), token))
 {

 Web currentWeb = ctxWorkspace.Web;
 ctxWorkspace.Load(currentWeb);
 ListCollection lists = ctxWorkspace.Web.Lists;
 ctxWorkspace.Load(lists);
 UserCollection webUsers = currentWeb.SiteUsers;
 ctxWorkspace.Load(webUsers);
 ctxWorkspace.ExecuteQuery();

 foreach (List list in lists)
 {
 list.NoCrawl = true;
 list.Update();
 }

 foreach (User user in webUsers)
 {
 if (user.LoginName.ToLowerInvariant().Contains("membership"))
 {
 if (!user.IsSiteAdmin)
 {
 webUsers.RemoveById(user.Id);
 }
 }
 }
 currentWeb.Update();
 ctxWorkspace.ExecuteQuery();

Office 365( O365 ) – Client application registration, authentication and authorization

OK,

I am currently working on a client side application (console app) that needs to connect to a O365 site and do some stuff with CSOM.

In this post I will write some basic things you need to do in order to achieve a connection to an O365 site. In my case I am working with a console app but this can be a powershell app also.

At first I recommend you look at this piece of code which you need to use to achieve the communication and authentication with O365:

https://github.com/OfficeDev/SharePoint-Power-Hour-Code-Samples/blob/master/SP.SiteCreatorWeb/TokenHelper.cs

https://github.com/OfficeDev/SharePoint-Power-Hour-Code-Samples

A basic C# code to connecto O365 and do get a list and its items would look like this:


Uri siteUri = new Uri(ConfigurationManager.AppSettings["SiteCollectionRequests_SiteUrl"]);

 //Get the realm for the URL
 string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);

 //Get the access token for the URL.
 //Requires this app to be registered with the tenant
 string accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, siteUri.Authority, realm).AccessToken;

 //Get client context with access token
 using (var ctx = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), accessToken))
 {

 // Set the time out as high as possible
 ctx.RequestTimeout = int.MaxValue;

 List list = ctx.Web.Lists.GetByTitle(ConfigurationManager.AppSettings["SiteCollectionRequests_List"]);
 CamlQuery camlQuery = new CamlQuery();
 camlQuery.ViewXml = "your CAML query here";
 ListItemCollection listItems = list.GetItems(camlQuery);
 ctx.Load(listItems);
 ctx.ExecuteQuery();

 var itemsCount = listItems.Count;

So before you can use your code you need to do two things in your target site:

  1. Register you client side app. This basically means that in your app config you need to set a clientID and a client secret. Without these values no proper authentication and authorization can done.
    1. Register you app in O365 by using the following URL and replacing the hostname and adding your target site: http://<SharePointWebsite>/_layouts/15/AppRegNew.aspx
  2. After you register the app you need to need to specify in a target site what kind of privileges the app has. In the code sample above you would need to specify at least read rights.
    1. To provide privileges the following url: http://<SharePointWebsite>/_layouts/15/AppInv.aspx
    2. Notice that for this step you need to provide a XML describing the privileges request. The simples way for me was to start up Visual Studio, create an app and define the rights request through the GUI and copy & pasting the XML from the AppMenifest.xml. It would look something like this:
      1. <AppPermissionRequests AllowAppOnlyPolicy=”true”>
        <AppPermissionRequest Scope=”http://sharepoint/content/sitecollection/web/list&#8221; Right=”FullControl” />
        </AppPermissionRequests>

Also the last thing which you need to check is to have a proper configurations in the app.config(if you are using an console app):


<appSettings>
 <add key="ClientId" value="client id obtained after registration" />
 <add key="ClientSecret" value="client secrect after registration" />
 <add key="SiteCollectionRequests_SiteUrl" value="yoursiteurl"/>
 <add key="SiteCollectionRequests_List" value="listname" />

 </appSettings>

Also notice that the ClientId and the ClientSecret have to be provided in the app.config for the TokenHelper.cs class to work. The class will search for these settings values automatically.

Additional help here: http://blog.vgrem.com/2015/01/27/consuming-the-sharepoint-online-rest-api-from-powershell-part-2/

https://msdn.microsoft.com/en-US/library/fp142383.aspx

https://msdn.microsoft.com/library/fp179892(office.15).aspx

https://msdn.microsoft.com/en-us/library/office/fp179912.aspx#BasicOps_SPListItemTasks

Record center problems with document library forms with custom content types and redirection rules

The problem is as follows to make it happen:

  1. You are having a custom document library created
  2. You are using a feature to assign a custom content type
  3. The library where the feature is assigning the content type has a redirection rule

If the above are true then what happened in my case was that the document library suddenly had in the New and Edit form content types which where not assigned to the document library but used in the redirection rules for the record center.

The only solution I found was to temporarily disable(delete) the redirection rules and re-activate my feature, well the library was a mess so I had to re-create it again.