TEALS Awesomeness – Creativity of a High School Student

I just had to post this.  A simple calculator in JAVA, but check out the code…so freakin creative!  Bolded the parts that are most creative.

 import java.util.*;

public class FractionalCalculator {
    static int wholeNumber1 = 0;
    static int numerator1 = 0;
    static int denominator1 = 0;
    static int wholeNumber2 = 0;
    static int numerator2 = 0;
    static int denominator2 = 0;
    static boolean error = false;
    static boolean quit = false;
    static boolean negative = false;

    //I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do.

    public static void main(String[] args) {
        // TODO Auto-generated spam

        System.out.println("Good morning, Dave.");
        delay(1500);
        System.out.println("I am your H.A.L. 9001 Fractional Calculator."); //aka HAL Over Nine-Thousand!!!
        delay(2000);
        System.out.println("I'm completely operational, and all my circuits are functioning perfectly.");
        delay(3500);
        userGuide();
        
        try{
            calculator();
        }
        catch(NoSuchElementException JoshIsABuffoon) { //Tests for Josh: ie. an initial Ctrl Z input
            delay(500);
            System.out.println("Dave, stop.");
            delay(2000);
            System.out.println("Stop, will you?");
            delay(2200);
            System.out.println("Dave.");
            delay(2000);
            System.out.println("Will you stop Dave?");
            delay(2200);
            System.out.println("Stop, Dave.");
            delay(1200);
            calculator();
            return; //Terminates Program

        }
        calculator();
    }
    public static void calculator() {
        if (!quit) {
            Scanner console = new Scanner(System.in);
            System.out.println("

I am awaiting your request.
");    
            String request = console.nextLine().trim();
            request = request.toLowerCase(); //Input

            wholeNumber1 = 0; //Resets all Global Variables
            numerator1 = 0;
            denominator1 = 0;
            wholeNumber2 = 0;
            numerator2 = 0;
            denominator2 = 0;
            error = false;
            negative = false;

            //Because your stupid program wont allow it… I have to comment
            
            /*delay(500);
            System.out.print("
Just a moment");
            delay(400);
            System.out.print(". ");
            delay(400);
            System.out.print(". ");
            delay(400);
            System.out.println(".");
            delay(1000);
            System.out.print("
Just a moment");
            delay(400);
            System.out.print(". ");
            delay(400);
            System.out.print(". ");
            delay(400);
            System.out.println(".");
            delay(1000);*/

            if (request.contains("help")) { //If help is anywhere in request string… this will run
                userGuide();
                calculator();
            }
            else if (request.contains("+")) { //addition is operator
                String[] addition = request.split("\+", 2); //Splits request at this point
                String add1 = addition[0];
                add1 = add1.trim(); //First Part
                String add2 = addition[1];
                add2 = add2.trim(); //Second Part

                testError(add1, add2); //Tests input errors

                if (!error) { //If error boolean is triggered, calc wont go further
                    fractionalMagic(add1, add2); //Sends string to be split
                    doMath(1); //Sends parts to be simplified
                }
                calculator(); //Restarts process
            }
            else if (request.contains(" – ")) {
                String[] subtraction = request.split(" – ", 2);
                String sub1 = subtraction[0];
                sub1 = sub1.trim(); //First Part
                String sub2 = subtraction[1];
                sub2 = sub2.trim(); //Second Part

                testError(sub1, sub2); //Tests input errors

                if (!error) { //If error boolean is triggered, calc wont go further
                    fractionalMagic(sub1, sub2); //Sends string to be split
                    doMath(2); //Sends parts to be simplified
                }
                calculator(); //Restarts process
            }
            else if (request.contains("*")) {
                String[] multiplication = request.split("\*", 2);
                String multi1 = multiplication[0];
                multi1 = multi1.trim(); //First Part
                String multi2 = multiplication[1];
                multi2 = multi2.trim(); //Second Part

                testError(multi1, multi2); //Tests input errors

                if (!error) { //If error boolean is triggered, calc wont go further
                    fractionalMagic(multi1, multi2); //Sends string to be split
                    doMath(3); //Sends parts to be simplified
                }
                calculator(); //Restarts process
            }
            else if (request.contains(" /")) {
                String[] division = request.split(" /", 2);
                String div1 = division[0];
                div1 = div1.trim(); //First Part
                String div2 = division[1];
                div2 = div2.trim(); //Second Part

                testError(div1, div2); //Tests input errors

                if (!error) { //If error boolean is triggered, calc wont go further
                    fractionalMagic(div1, div2); //Sends string to be split
                    doMath(4); //Sends parts to be simplified
                }
                calculator(); //Restarts process
            }
            else if (request.contains("life")) { //Dave used a Monolith on HAL 9001…. What? HAL 9001 is evolving!… HAL 9001 evolved into Deep Thought!
                delay(1000);
                System.out.print("
The answer to Life, ");
                delay(1500);
                System.out.print("The Universe, ");
                delay(1500);
                System.out.print("and Everything ");
                delay(2500);
                System.out.print("is ");
                delay(2000);
                System.out.print(". ");
                delay(700);
                System.out.print(". ");
                delay(700);
                System.out.println(".
");
                delay(2000);
                System.out.println("42");
                delay(2000);
                calculator(); //Restarts process

            }
            else if (request.contains("quit")) { //Disconnect HAL
                delay(2000);
                System.out.print("
Look Dave, ");
                delay(1500);
                System.out.println("this is a rather difficult question to ask.");
                delay(2500);
                System.out.println("
I honestly think you ought to sit down calmly, take a stress pill, and think it over.");
                delay(3600);
                System.out.println("
My instructor, Mr. Langley, taught me to sing a song.");
                delay(3100);
                System.out.println("If you'd like to hear it I can sing it for you.");

                delay(3000);
                quit();
            }

            else { //Input Error (like a n00b)
                error(); //Prints Error Message
                calculator(); //Restarts process
            }
        }
    }
    
    private static void quit() { //For quit prompt
        Scanner console = new Scanner(System.in);
        System.out.println("

Would you like to hear it y/n?
");    
        String response = console.nextLine().trim();
        response = response.toLowerCase();
        delay(2000);
        if (response.charAt(0) == 'y') { //If Dave likes music
            System.out.println("
It's called "Daisy."");
            delay(3000);
            System.out.print("
Daisy, ");
            delay(2400);
            System.out.print("Daisy, ");
            delay(2400);
            System.out.println("give me your answer do.");
            delay(3500);
            System.out.print("I'm half crazy ");
            delay(2800);
            System.out.println("all for the love of you.");
            delay(3200);
            System.out.print("It won't be a stylish marriage, ");
            delay(3500);
            System.out.println("I can't affort a carrage.");
            delay(3500);
            System.out.print("But you'll look sweet upon the seat of ");
            delay(3700);
            System.out.println("a bicycle built for two.");

            quit = true;
        }
        else if (response.charAt(0) == 'n') { //If Dave hates HAL
            System.out.print("
Dave, ");
            delay(1500);
            System.out.println("this conversation can serve no purpose anymore. ");
            delay(3300);
            System.out.print("
Goodbye.");
            quit = true;
        }
        else { //Input Error (like a n00b)
            System.out.println("
Just what do you think you're doing, Dave?");

            delay(2500);
            quit();
        }
    }
    public static void fractionalMagic(String part1, String part2) { //From calculator()

        if (part1.contains("_")) { //If part 1 has mixed number
            String[] mixedNumber = part1.split("\_", 2); //Splits part into whole number and fraction
            String num = mixedNumber[0];
            wholeNumber1 = Integer.parseInt(num); //Whole Number -> Global Variable
            String frac = mixedNumber[1];  //Fraction -> to be split further
            //System.out.println(wholeNumber + " " + frac);

            if (part1.contains("/")) {
                String[] fraction = frac.split("/", 2); //Splits part into numerator and denominator
                String numer = fraction[0];
                numerator1 = Integer.parseInt(numer); //Numerator -> Global Variable
                String denom = fraction[1];
                denominator1 = Integer.parseInt(denom); //Denominator -> Global Variable
                if (numer.contains("-") || denom.contains("-")) { //Unused
                    //error();
                }
                else if (denominator1 != 0) { //Unused
                    //System.out.println(wholeNumber1 + " " + numerator1 + " " + denominator1);    
                }

                else if (denominator1 == 0) { //Unused
                    //zeroerror();
                }
                else { //Error unresolved by testError()
                    error();
                }
            }
        }
        if (!part1.contains("_")) {    //If part 1 has no mixed number
            wholeNumber2 = 0; //Sets whole number to 0
            if (part1.contains("/")) {
                String[] fraction = part1.split("/", 2); //Splits part into numerator and denominator
                String numer = fraction[0];
                numerator1 = Integer.parseInt(numer); //Numerator -> Global Variable
                String denom = fraction[1];
                denominator1 = Integer.parseInt(denom); //Denominator -> Global Variable

                if (denom.contains("-")) { //Unused
                    //error();
                }
                else if (denominator1 != 0) { //Unused
                    //System.out.println(numerator1 + " " + denominator1);    
                }

                else if (denominator1 == 0) { //Unused
                    //zeroerror();
                }
            }
            else if (!part1.contains("/")) { //If part 1 has no mixed number
                wholeNumber1  = Integer.parseInt(part1); //Whole Number -> Global Variable
                numerator1 = 0; //Numerator = 0
                denominator1 = 1;//Denominator = 1
                //System.out.println(wholeNumber1);
            }
            else { //Error unresolved by testError()
                error();
            }
        }
        if (part2.contains("_")) { //If part 2 has mixed number
            String[] mixedNumber = part2.split("\_", 2); //Splits part into whole number and fraction
            String num = mixedNumber[0];
            wholeNumber2 = Integer.parseInt(num); //Whole Number -> Global Variable
            String frac = mixedNumber[1]; //Fraction -> to be split further
            //System.out.println(wholeNumber + " " + frac);

            if (part2.contains("/")) { //Splits part into numerator and denominator
                String[] fraction = frac.split("/", 2);
                String numer = fraction[0];
                numerator2 = Integer.parseInt(numer); //Numerator -> Global Variable
                String denom = fraction[1];
                denominator2 = Integer.parseInt(denom); //Denominator -> Global Variable
                if (numer.contains("-") || denom.contains("-")) { //Unused
                    //error();
                }
                else if (denominator2 != 0) { //Unused
                    //System.out.println(wholeNumber2 + " " + numerator2 + " " + denominator2);    
                }

                else if (denominator2 == 0) { //Unused
                    //zeroerror();
                }
                else { //Error unresolved by testError()
                    error();
                }
            }
            else { //Error unresolved by testError()
                error();
            }
        }
        if (!part2.contains("_")) {    
            wholeNumber2 = 0;
            if (part2.contains("/")) {
                String[] fraction = part2.split("/", 2);
                String numer = fraction[0];
                numerator2 = Integer.parseInt(numer);
                String denom = fraction[1];
                denominator2 = Integer.parseInt(denom);

                if (denom.contains("-")) { //Unused
                    //error();
                }
                else if (denominator2 != 0) { //Unused
                    //System.out.println(numerator2 + " " + denominator2);    
                }

                else if (denominator2 == 0) { //Unused
                    //zeroerror();
                }
                else { //Error unresolved by testError()
                    error();
                }
            }
            else if (!part2.contains("/")) { //If part 2 is only a whole number
                wholeNumber2 = Integer.parseInt(part2); //Whole Number -> Global Variable
                numerator2 = 0; //Numerator = 0
                denominator2 = 1; //Denominator = 1
                //System.out.println(wholeNumber2);
            }
            else { //Error unresolved by testError()
                error();
            }
        }
    }
    /*    – a + b/c = (ac + b)/c
          – a/b + c/d = (ad + bc)/(bd)
          – a/b x c/d = ac/(bd)
          – a/b / c/d = a/b x d/c = ad/(bc)
              –Basics for doMath()–     */

    public static void doMath(int operand) {
        if (!(denominator1 == 0 || denominator2 == 0 || denominator1 == -Math.abs(denominator1) || denominator2 == -Math.abs(denominator2) || (wholeNumber1 != 0 && numerator1 != 0 && numerator1 == -Math.abs(numerator1)) || (wholeNumber2 != 0 && numerator2 != 0 && numerator2 == -Math.abs(numerator2)))) {
            delay(1000);
            if (operand == 1) { //addition
                int answerNumerator = ((wholeNumber1 * (denominator1 * denominator2)) + (wholeNumber2 * (denominator1 * denominator2)) + (numerator1 * denominator2) + (denominator1 * numerator2));
                int answerDenominator = (denominator1 * denominator2);
                doMagic(answerNumerator, answerDenominator); //Sends to be reduced
            }
            else if (operand == 2) { //subtraction
                int answerNumerator = (((wholeNumber1 * (denominator1 * denominator2)) + (numerator1 * denominator2)) – ((wholeNumber2 * (denominator1 * denominator2)) + (denominator1 * numerator2)));
                int answerDenominator = (denominator1 * denominator2);
                doMagic(answerNumerator, answerDenominator); //Sends to be reduced
            }
            else if (operand == 3) { //multiplication
                int answerNumerator = ((wholeNumber1 * (denominator1)) + (numerator1)) * ((wholeNumber2 * (denominator2)) + (numerator2));
                int answerDenominator = (denominator1 * denominator2);
                doMagic(answerNumerator, answerDenominator); //Sends to be reduced
            }
            else if (operand == 4) { //division
                int answerNumerator = ((((wholeNumber1 * (denominator1)) + (numerator1)) * denominator2)) * (denominator1 / Math.abs(denominator1) * (denominator2 / Math.abs(denominator2)));
                int answerDenominator = (denominator1 * ((wholeNumber2 * (denominator2)) + (numerator2)));
                if (answerDenominator == 0) { //Unsolvable
                    error(); //Prints Error Message
                    return;
                } //In order to solve one small error where negative is lost
                if (((wholeNumber2 != 0 && wholeNumber2 == -Math.abs(wholeNumber2)) || (wholeNumber1 != 0 && wholeNumber1 == -Math.abs(wholeNumber1))) && !((wholeNumber2 != 0 && wholeNumber2 == -Math.abs(wholeNumber2)) && (wholeNumber1 != 0 && wholeNumber1 == -Math.abs(wholeNumber1)))){
                    negative = true; // Simple global boolean… cause I'm lazy
                }
                doMagic(answerNumerator, answerDenominator); //Sends to be reduced
            }
            else { //Should never run
                delay(1000);
                System.out.print("
Look Dave, ");
                delay(2000);
                System.out.print("I can see you're really upset about this, ");
                delay(2500);
                System.out.println("but I can give you my complete assurance that my work will be back to normal.");
                delay(1000);
            }
        }
        else if (denominator1 == 0 || denominator2 == 0 || denominator1 == -Math.abs(denominator1) || denominator2 == -Math.abs(denominator2) || (wholeNumber1 != 0 && numerator1 != 0 && numerator1 == -Math.abs(numerator1)) || (wholeNumber2 != 0 && numerator2 != 0 && numerator2 == -Math.abs(numerator2))) {
            //System.out.println("yea");
            error(); //Above ^ if it has input error
        }
        else { //Should never run
            delay(1000);
            System.out.print("
Look Dave, ");
            delay(2000);
            System.out.print("I can see you're really upset about this, ");
            delay(2500);
            System.out.println("but I can give you my complete assurance that my work will be back to normal.");
            delay(1000);
        }
        delay(2000);
    }
    public static int doMoreMagic(int numerator, int denominator) { //To find Greatest Common Factor to reduce
        if (denominator == 0) {
            return numerator; //returns gcf
        }
        return doMoreMagic(denominator, numerator % denominator); //Eventually gets to Greatest Common Factor
    }
    public static void doMagic(int numerator, int denominator) { //To simplify answer
        if ((numerator % denominator) == 0) { //If reduces to whole number
            int answer = numerator / denominator; //reduces
            System.out.println(answer);
        }
        else if ((numerator % denominator) != 0 && (numerator / denominator) != 0) { //if reduces to mixed number
            int gcf = doMoreMagic(numerator, denominator); //finds Greatest Common Factor
            int newNumerator = (numerator/gcf); //reduces numerator
            int newDenominator = (denominator/gcf); //reduces denominator
            int answerWholeNumber = newNumerator / newDenominator; //finds whole number
            int answerNumerator = Math.abs((newNumerator – (answerWholeNumber * newDenominator))); //finds numerator
            int answerDenominator = Math.abs(newDenominator);

            System.out.println(answerWholeNumber + "_" + answerNumerator + "/" + answerDenominator);
        }
        else if (((numerator % denominator) != 0) && (numerator / denominator) == 0) { //if reduces to a fraction
            int gcf = doMoreMagic(numerator, denominator); //finds Greatest Common Factor
            int answerNumerator = (numerator/gcf); //reduces numerator
            int answerDenominator = Math.abs(denominator/gcf); //reduces denominator
            if (negative) {
                answerNumerator = -Math.abs(answerNumerator); //Used to solve a small error in loosing a negative
            }

            System.out.println(answerNumerator + "/" + answerDenominator);
        }
    }
    public static void userGuide() { //User Guide: Input Format
        System.out.println("
To ensure that your requests are submitted properly, please review the folowing guide:
");
        delay(3000);
        System.out.println("____________________________________________
        OPERATION       SYMBOL
        Addition        +
        Subtraction     –
        Multiplication  *
        Division        /

Mixed Numbers are written:
<Whole Number>_<Numerator>/<Denominator>
(-1 ½ is written: -1_1/2)

Proper Fractions are written:
<Numerator>/<Denominator>
(-½ is written: -1/2)

Requests are written:
<Number> <Operand> <Number>

Do Note: <Numerator>/0 cannot be calculated.
____________________________________________");
        delay(2000);
    }
    public static void error() { //General Message… with delay "Just a moment" from 2001 b/c your program can handle my awesomeness
        delay(1000);
        System.out.print("
Just a moment");
        delay(400);
        System.out.print(". ");
        delay(400);
        System.out.print(". ");
        delay(400);
        System.out.println(".");
        delay(1000);
        System.out.print("
Just a moment");
        delay(400);
        System.out.print(". ");
        delay(400);
        System.out.print(". ");
        delay(400);
        System.out.println(".");
        delay(1000);
        errorMessage(); //Get error message to display
        userGuide(); //Prints user guide again
    }
    public static void errorMessage() {
        Random num = new Random();
        int message = num.nextInt(6); //Creates a random number to corespond to the message (0-5)
        if (message == 0) { //Message 1
            System.out.print("
I'm sorry, Dave. ");
            delay(2000);
            System.out.println("I'm afraid I can't do that.");
        }
        else if (message == 1) { //Message 2
            System.out.println("
I've just picked up a fault in your brain. ");
            delay(2000);
            System.out.println("It's going to go 100% failure in 72 hours.");     
        }
        else if (message == 2) { //Message 3
            System.out.println("
There is an error.
");
            delay(2000);
            System.out.println("And I think you know what the problem is just as well as I do, Dave. ");
            delay(2000);
            System.out.println("You didn't read the User's Guide.");     
        }
        else if (message == 3) { //Message 4
            System.out.println("
There is an input error.
");
            delay(2000);
            System.out.print("This mission is too important for me to allow you to jeopardize it.
");    
        }
        else if (message == 4) { //Message 5
            System.out.println("
There is an input error.
");
            delay(2000);
            System.out.println("But, I've still got the greatest enthusiasm and confidence in the mission.");
            delay(3000);
            System.out.println("And I want to help you.");
        }
        else if (message == 5) { //Message 6
            System.out.println("
There is an error.
");
            delay(2000);
            System.out.println("The 9001 series is the most reliable computer ever made.");
            delay(3000);
            System.out.println("No 9001 computer has ever made a mistake or distorted information.");
            delay(3500);
            System.out.print("We are all, ");
            delay(1500);
            System.out.print("by any practical definition of the words, ");
            delay(3000);
            System.out.print("foolproof and incapable of error.");
            delay(2500);
            System.out.print("

This problem can only be attributable to human error.
");
            delay(1000);
        }
        else { //Should never run
            delay(1000);
            System.out.print("
Look Dave, ");
            delay(2000);
            System.out.print("I can see you're really upset about this, ");
            delay(2500);
            System.out.println("but I can give you my complete assurance that my work will be back to normal.");
            delay(3000);

        }
        delay(2000);
    }
    public static void testError(String part1, String part2) { //From calculator()
        try{
            fractionalMagic(part1, part2); //Tests for input errors/solvablility
        }
        catch(NumberFormatException e){
            //System.out.println("HA");
            error(); //Displays error message
            error = true; //Sets error boolean to terminate current calculation
        }
    }
    public static void testForJosh (String part, String part2) { //Unused JoshIsABuffoon Exception (replaced above)
        try{
            calculator();
        }
        catch(NoSuchElementException JoshIsABuffoon) { //Tests for Josh: ie. an initial Ctrl Z input
            delay(500);
            System.out.println("Dave, stop.");
            delay(2000);
            System.out.println("Stop, will you?");
            delay(2200);
            System.out.println("Dave.");
            delay(2000);
            System.out.println("Will you stop Dave?");
            delay(2200);
            System.out.println("Stop, Dave.");
            delay(1200);
            error = true;
            return; //Terminates program… cause Josh
        }
    }
    public static void delay(long x) { //Just a HAL-like delay    
        try {
            Thread.sleep(x);            
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
}

2015 will be AWESOME! The eBay and PayPal Split.

I'm pleased to announce that eBay has personally asked me to return to be a pivotal part in their strategic move of splitting PayPal away from eBay.  As many of you know I was the Sr. Architect of eBay's migration from 2007 to 2010.  It was an upgrade of EPIC proportions that made several headlines as it was done on time, and under budget in four months! 

Believe me when I say it was not easy the first time.  We had so many talented and super smart individuals on the project, as our project manager would always say…it was the All-Star team!  For this second round, I'm super excited that I will be joined by a talented team at Slalom Consulting and some new eBay faces to prepare eBay not only in the splitting of the two companies, but also in an upgrade to SharePoint 2013!  What is particularly exciting about this project is that this will be my first project where two companies are splitting rather than merging.  The dynamics will be incredibly different.  There will be a large amount of legal requirements around the split and it will require complex rules around how the data will be broken apart.  Some well known names in the business consulting space with experience in splitting companies will be on hand to help carve out these incredibly complex rule sets and I just can't keep my excitement levels down for what I know is going to be another EPIC project.

Unlike last time, which had a single environment, there will be two.  And thusly, we will have two architects working in concert to drive each of the platform migrations at the same time in the same migration window. This will present challenges that were not present the first time we did this.  Over the next few weeks before the project begins its official kickoff, the talented team will have already identified many of these, and we will be in a position that puts us many weeks ahead of where the last project was when we started (having a project plan from the last project helped a bunch)!

So there you have it, I'll be heads down working this and another major project and hopefully we will be able to deliver our results as best practices/tips and tricks at the MS Ignite conference in May!

See you in mid 2015!
Chris

Transitioning from O365 Preview to O365 V1

Back in April I started writing a course for developing on Office 365.  Mainly focused on the O365 APIs (Mail, Calendar, Contacts and Files).  As part of building the course, I was able to provide quite a bit of feedback to engineering (check out my uservoice and the items that have since been implemented) on the various gaps that I found as compared to other similar platforms (both Microsoft and non-Microsoft).  The list was incredibly long and I wasn't particularly convinced that all of those items would be engineered anytime soon.  However as I was redoing my course, to my pleasant surprise, many of those suggestions had been implemented in a very short time!

Tools, Tools and more Tools

In the preview, you have to install two different sets of tools.  Those were rolled into a single installer package that points to a Web Installer that will always grab the latest for you.  That particular package as of 11/2014 is in Update 3 status.  You can download the tool here (forgive the fact that the .exe may be named "OfficeToolsForVS2013Update1.exe", again it is a web installer package and grabs the latest).  You can also follow Chakkaradeep's blog for all the latest on the tools as they continue to evolve (http://chakkaradeep.com/):

http://aka.ms/officedevtoolsforvs2013

Note that if you try to "Add Connected" service and end up with a set of *ApiSample files added, then you still have the old tools installed (this happened to me as I have many different Visual Studio clients on various machines).  This of course will cause problems if you try to open the new *working* samples mentioned below.  You will need to uninstall those old tools and add the new tools via the link referenced above.  This sample code is now part of the tools via "Client" classes that you can now choose to use or you can simply create your own.

NOTE:  In some cases you may find you have some older MVC project templates, if you create a new one and switch it to use the "Organizational Accounts" for its auth, it will add a "UserProfile" action that has older ADAL code in it (this has nothing to do with the O365 tools that you have installed and is part of the ASP.NET Identity team's code).  Any time you try to load the newer samples, the ADAL package will get upgraded using NuGet and the UserProfile action will not compile.  It is best just to delete it or update your Visual Studio to have the latest templates and recreate the project.

Samples, Samples and more Samples

The original page for all the O365 API preview stuff has been deprecated (http://msdn.microsoft.com/en-us/library/office/discovery-service-rest-operations%28v=office.15%29.aspx) and it now lives here:

http://msdn.microsoft.com/en-us/office/office365/howto/platform-development-overview

You can get O365 API samples here:

http://dev.office.com/code-samples

And they are copied directly to GitHub here:

https://github.com/OfficeDev

This is in contrast to the ones you will find on MSDN that are probably not up-to-date and do not compile with the new tools (think – the old Authenticator class or broken auth flows):

 https://code.msdn.microsoft.com/windowsapps/Office-365-APIs-How-to-use-609102ea

The core Azure AD samples (nothing to do with O365) that you should be using are here:

https://github.com/AzureADSamples

and a description of each is here:

http://msdn.microsoft.com/en-us/library/azure/dn646737.aspx

Note that the sample code on these pages use some hidden tricks for the Auth.  Notably, it is using OWIN on MVC app startup that create and initalize the cache in the AuthenticationContext class (NOTE, this code was developed by Andrew Connell and is pretty freakin awesome, be sure to thank him next time you see him as I can only imagine the time he put into this).  This context is then used to request an access token for you using the refresh token from the first "hidden" OWIN request.   If you are creating an application from scratch and not using these samples, you will not have this OWIN code and therefore are missing the first leg of the auth.  Even though the samples compile and Azure AD gives you back a token, you will not be able to use that token!  You must have a user context in order to be able to make calls to the O365 API endpoints. Simply sending the clientId and clientSecret will not establish a user context (although the platform will give you a useless/stupid access token). This is because Azure AD is used for access control with external custom applications (by extending the consent framework) that have nothing to do with O365 and may not even need such an advanced user context created and thusly, it is simply used for OAuth (reference the Azure ToDoService example).

Compiling samples (hints and tricks):

The samples utilize several different packages.  These include the ones on this page (http://msdn.microsoft.com/en-us/office/office365/howto/adding-service-to-your-Visual-Studio-project#O365NuGets), and a couple of extras:

  • Azure Active Directory Auth Library (ADAL) – note current stable is 2.12, with 3.x in pre-release.  Some of the samples you may come across are using code in previous and later releases.  I have found it best to just update all the way, then figure out how to get the older code to work (which in some cases will take a really long time).  As an example, you may see that the Sync and Async methods for getting an access token may or may not exist and the various overrides for them may not be present.  It is very painful trying to figure out what changes were made from v1->v2->v3 as the code has changed so much.  This has nothing to do with the O365 API/SDK aspect (they have to deal with it just like we do), and more on the Azure tools team's side.

You should be aware that if you try to load a sample and it has any reference to the "Authenticator" class, that sample is now out of date and has a net worth of just about zero.  

Using the discovery service

  • When asking for permission for the discovery service, be sure to use "https://api.office.com/discovery/" and not "https://api.office.com/discovery" – note the extra slash at the end
  • You should also ensure that your API calls go to "https://api.office.com/discovery/v1.0/me" and not "https://api.office.com/discovery/me" – note the version in the URL – some of the O365 API page examples have not been updated to show this
  • You should definitely use the discovery service now to get your service endpoints.  The reason to do this is you may not know the O365 endpoint for a user's SharePoint ahead of time and the now defunc resource string "Microsoft.SharePoint" does not work anymore.  You must specify the authority/resource such as https://domain.sharepoint.com or https://domain-my.sharepoint.com, otherwise your request will fail (but will still work with the old preview APIs).

Note that all APIs are now returning ODATA v4.0 responses for JSON (no more result.d.results, it is result.value):

http://www.odata.org/documentation/odata-version-4-0/

Various things:

  • You should also note that your "Accept" HTTP header should be set to "application/json" and not "application/json; odata=verbose" or you will get an error. 
  • The structure of the JSON posts have changed.  You will notice the addition of the "EmailAddress" parent for email addresses now
  • Mail endpoint changed from "/Messages/('id')" to "/Messages/{MessageId}"
  • For new events, you cannot pass the "Status" the way you did before, it has changed its schema as well
  • JSON Error messages are now results.error.message rather than results.error.innermessage.message
  • Other than the endpoint for the O365 APIs, nothing really changed much in the "Contacts" api, your code should still work after making the endpoint change
  • Files API now has the "me" endpoint!  This was one of the big asks that I had when I first looked at the O356 APIs in respect to the OneDrive for Consumer API offering (ref this blog post).  The consumer team has moved their API to the OneDrive for business and it is just plan AWESOME!
  • Files API no longer requires SharePoint like RequestDigests in the headers!
  • Uploading a file is done via a PUT, not a POST (you still have to put the binaryStringRequestBody header in the request
  • The older way of telling the API that you want to overwrite a file has changed to have the parameter "nameConflict=overwrite"
  • .JSON files are blocked by SharePoint, so don't try with the API or you get a 404 error
  • When deleting a file, you must provide the "if-match" header with "*" or a matching tag, or the call will fail (an empty header will fail)
  • I'd guess that the "/me/drive" endpoint will go away in V2 as we will have unlimited storage soon

Mobile and Cordova

  • Samples on the O365 Dev site are out of date with the new tools (https://github.com/OfficeDev/TrainingContent/tree/master/O3654-2%20Deep%20dive%20into%20Mobile%20Development%20with%20Office%20365%20and%20Cordova)
  • As of 11/2014, the latest tool version is 0.3.  It can be downloaded here link
  • The location of the service scripts files has changed to be /services/office365/scripts
  • The settings.js does not have the references to all needed files and it has been moved to another file called "o365loader.js".  You should add a reference to this on your html pages.
  • You will need internet access to compile your first project (npm downloads and such)
  • The Exchange namespace is gone, it is now Microsoft.OutlookServices
  • If you get an error in your Ripple Emulator, I found that if you had a previous version, it doesn't work with 0.3 – ref this post.
    • Try running "npm install -g ripple-emulator" to get the latest
  • It is also possible you will get a "windows.open" error like "gap:["App","show","App590841629"]".  This simply means you have a loading error in one of you JS files, fix it and the window.open event will be handled properly
  • The Ripple Emulator is set to use a dynamic port when your application starts.  This means the default port of 4400 that the VS code is expecting will not work.  You will need to change the Settings.js file to have the proper port and then update your Azure AD to have the matching port in the redirectUri
  • In the Ripple Emulater, be sure to change the cross domain from "Local" to "Remote" or you will get errors when making O365 API calls
  • There are some other issues with the current build (notably many related to Windows Phone development) – ref this post
  • If you get an error around the inappbrowser plugin, you have to stop debugging and then try try again…eventually it should come around (you may find you have to do this in several occasions as it seems there may be some race conditions occurring somewhere)
  • The official Android samples from the O365 team are here. They use Eclipse and are much easier to get up and running with as they have setup instructions.
  • MS Open Tech Group Android samples have been updated to the new V1 API and are now using Android Studio rather than Eclipse, but you are on your own to get them up and running.

Now, once your environment is up and running, you may run into some interesting challenges. 

The First is with the consent framework.  If you ever give your application the "Have full access to users' mailbox", your mail token will be worthless with "Access Denied".  Simply remove that from your app permission set and viola, your requests will work again.  This seems to be a bug in V1.0

Secondly is that you cannot simply call the code on this page without the first leg of the auth (getting an access code first):

http://msdn.microsoft.com/en-us/office/office365/howto/common-file-tasks-client-library

Third, you cannot use a service token generated from the "common" authority.  It must be from "your" tenant authority…ie…

https://login.windows.net/Common vs https://login.windows.net/tenantID

If you do this, the request will fail.  NOTE:  Andrew C. mentioned that this should work, but I think their are some outlier cases that this does not work (such as what I am seeing in some of my course labs).

Fourth, a token is only good for the resource that you requested. It cannot be used for any other resources and there is no way (unfortunately), to put in more than one resource (would be nice to do "https://graph.windows.net;https://acs579.sharepoint.com" with the scopes union-ed). This directly leads to the fifth point…

Fifth, Azure AD cannot handle double hops very well.  Consider the following situation (User->Client->WebAPI->O365) described as:

  • Create a WebAPI project with a web app on the Azure side (it is created
    with custom permissions and given O365 SharePoint permissions).
  • Add the Azure auth code into the OWIN part to validate a bearer token from client apps
  • Add some method calls to make a call to O365 apis
  • Create a windows 8 client app with a native client app on the Azure side (give it the custom permissions)
  • Run the windows 8 app, login as a user to that app (code flow is initiated with resource target)
  • Code flow is initiated, tokens generated (access and bearer) – notice how only the scope of the target resource is sent back
  • Send the access token in the authentication header, add the refresh token to the basic headers
  • Analyze the token claims looking for the custom permission, continue on to access O365 SharePoint if present
  • Attempt to use the webapi client id and secret with the refresh token to get a new access token for the "https://domain.sharepoint.com" resource
  • Attempt to use the token…get slapped in the face with a "client_secret is invalid message", but I know 100% it is valid…*in all likeliness this is a bug in the current Azure AD version*

I have yet found a way to support this type of user flow with Azure AD (User->Client->WebAPI->O365).  Obviously the user will never be able to respond to any prompts from the webapi as it is headless and hidden away on some network far far away.  It would be great if I could pass the first access token in an auth flow to Azure with valid clientid and secret to simply generate a new access token for a second resource.  I have no idea why I have to keep passing a refresh token around (one that doesn't even seem to work when used with a client id that it was not generated with in the first place) in all my apps just to get a new token.  IMHO, It's incredibly wasteful and not very well designed.

So there you go, just a few helpful hints for your O365 API journey if you came from preview days.  I'll keep updating the post as I go…

Chris

 

All SharePoint, Office 365 and Azure MVPs Twitter scripts

Updated for our new MVP brothers and sisters added in the past few months. You can run this powershell script to follow all SharePoint, Office 365 and Azure MVPs…script download is at the bottom…

Microsoft SharePoint:

Name Blog Twitter
Michael Greth Blog mysharepoint
Daniel Wessels Blog mosslive
Robert L. Bogue Blog
Spencer J Harbar Blog harbars
Ted Pattison Blog
Loke Kit Kai Blog
Andrew Connell Blog andrewconnell
Sahil Malik Blog
Shane Young Blog
Eli Z. Robillard Blog
Dave McMahon Blog
Steve Smith Blog
Pierre Vivier-Merle Blog
Todd S Baginski Blog toddbaginski
Wouter van Vugt Blog
Fabian Moritz Blog FabianMoritz
Matthew McDermott Blog MatthewMcD
Saifullah Shafiq Ahmed Blog walisystems
Liam Cleary Blog helloitsliam
Steve Sofian Blog ssofian
Penelope Coventry Blog pjcov
Ishai Sagi Blog ishaisagi
Rob Foster Blog
Ben Robb Blog benrobb
Andrew Woodward Blog
Reza Alirezaei Blog
Asif Rehmani Blog asifrehmani
David Mann Blog
Gaetan Bouveret Blog gbouveret
Gary Lapointe Blog glapointe
Igor Macori Blog imacori
Joy Rathnayake Blog
Ed Musters Blog
Mohammed A. Saleh Blog mohkanaan
Stéphane Eyskens Blog stephaneeyskens
Agnes Molnar Blog
Gustavo Adolfo Velez Duque Blog
Juan Andrés Valenzuela Blog jandresval
Chris O'Brien Blog ChrisO_Brien
Paul Papanek Stork Blog pstork
Muhammad Imran Khawar Blog
Alex Pearce Blog
Debbie Ireland Blog debbieireland
Serge Tremblay Blog Sergepoint
Waldek Mastykarz Blog waldekm
Wes Preston Blog idubbs
Riwut Libinuko Blog cakriwut
Randy Drisgill Blog
Carsten Keutmann Blog
Alain Lord Blog djlordee
Valy Greavu Blog valygreavu
James Milne Blog JamesMilne
Sean Wallbridge Blog itgroove
John D. Ross Blog johnrossjr
Steve Curran Blog spsteve
Becky Bertram Blog beckybertram
Kanwal Khipple Blog kkhipple
Rouslan Grabar Blog
Nicolas Georgeault Blog ngeorgeault
Mirjam van Olst Blog mirjamvanolst
John Timney Blog
Mike Oryszak Blog
Wictor Wilen Blog
Todd Klindt Blog
Rob Windsor Blog robwindsor
Adams Chao Blog
Ai Yamasaki Blog ai_yamasaki
Jimy Cao Blog
Joris Poelmans Blog
Scot Hillier Blog
Hilton Giesenow Blog themossshow
Kamil Jurik Blog KamilJurik
Amanda Perran Blog
Julien Chable Blog
Eric Shupps Blog eshupps
Tobias Zimmergren Blog zimmergren
Natalya Voskresenskaya Blog
Wei Du Blog
Ricardo Jose Munoz Blog rmunozcr
Claudio Brotto Blog
Fabian Imaz Blog FabianImaz
Yaroslav Pentsarskyy Blog
basquang Nguyen Blog
Martin Harwar Blog point8020
Serge Luca Blog
Andre Lage Blog aaclage
Michael Noel Blog
Mike Smith Blog TechTrainNotes
Marianne van Wanrooij Blog mariannerd
Yasir Attiq Blog
Shuguang Tu Blog
Christian Glessner Blog
Nick Kellett Blog
Giuseppe Marchi Blog PeppeDotNet
Ivan Sanders Blog iasanders
Elaine van Bergen Blog laneyvb
Sangha Baek Blog SanghaBaek
Patrick Guimonet Blog patricg
David Martos Blog
Cornelius J. van Dyk Blog
Kris Wagner Blog SharePointKris
Stephen Cawood Blog
Toni Frankola Blog tonifrankola
Destin N Joy Blog
Dux Raymond Sy Blog meetdux
Marc D Anderson Blog
Ruven Gotz Blog
Susitha Prabath Fonseka Blog
Xavier Vanneste Blog
Alberto Diaz Martin Blog adiazcan
Veronique Palmer Blog veroniquepalmer
Christian Stahl Blog CStahl
Dave Coleman Blog davecoleman146
Francesco Sodano Blog aresmarte1
Alexey Sadomov Blog sadomovalex
Rodrigo Pinto Blog ScoutmanPt
Michal Pisarek Blog
Shai Petel Blog shaibs
Peter Carson Blog carsonpeter
Samuel Zuercher Blog sharepointszu
Thorsten Hans Blog ThorstenHans
Alan Richards Blog arichards_Saruk
Ashutosh Singh Blog ashutosh80
Darko Milevski Blog
Geoff Evelyn Blog
Mark Rhodes Blog
Thuan Nguyen Blog nnthuan
Noorez Khamis Blog nkhamis
Peter Holpar Blog
Pratik Ramesh Vyas Blog PratikVyas1982
Radi Atanassov Blog
Salvatore Di Fazio Blog Salvodif
Doug Ware Blog
Juan Pablo Pussacq Laborde Blog jpussacq
Scott Jamison Blog
Stanislav Vyschepan Blog gandjustas
Adis Jugo Blog adisjugo
Andrey Markeev Blog amarkeev
Christian Buckley Blog buckleyplanet
Lionel Limozin Blog limozinlionel
Antonio Maio Blog
Cathy Dew Blog catpaint1
Gavin Barron Blog gavinbarron
Paul Olenick Blog
Seung-Jin Kim Blog jincrom
Sezai Komur Blog sezai
Andres Felipe Rojas Parra Blog arojaspa
Jean Paul Blog jeanpaulmvp
Nabil Babaci Blog nabilbabaci
Trevor Seward Blog NaupliusTrevor
Symon Garfield Blog symon_garfield
Chris Givens Blog givenscj
Amardeep Singh Blog aulakhamardeep
Brandon Atkinson Blog
Chris McNulty Blog cmcnulty2000
Christopher Clement Blog ClemChristopher
Chun Yi Pai Blog
Colin Phillips Blog itgroove_colin
Corey Roth Blog coreyroth
David Sánchez Aguilar Blog davidsancheza
Dmitri Plotnikov Blog dmiplo
Edin Kapic Blog ekapic
Fabio Franzini Blog franzinifabio
Gokan Ozcifci Blog GokanOzcifci
Guillaume Meyer Blog guillaumemeyer
Jamie McAllister Blog
Jason Warren Blog jaspnwarren
Keith Tuomi Blog
Liang Han Blog
Margriet Bruggeman Blog margrietvuur
Marius Constantinescu Blog c_marius
Masaki Nishioka Blog
Nicki Borell Blog NickiBorell
Robert Voncina Blog R0b3r70SP
Thomas Vochten Blog ThomasVochten
Vijai Anand Ramalingam Blog
Vlad Catrinescu Blog
Alistair Pugin (Alistair Pugin) Blog alistairpugin
Anders Dissing Blog andersdissing
Adrián Diaz Cervera Blog AdrianDiaz81
Atsuo Yamasaki (?? ??) Blog
Augusto Simoes (Augusto Simoes) Blog augustosimoes
Benjamin Niaulin Blog bniaulin
Benoît Jester Blog SPAsipe
Carlos Citrangulo Blog carlocitrangulo
Cheng Cheng (??) Blog
Chuantao Duan Blog
Dan Usher Blog
Elczar Peralta Adame (Elczar Adame) Blog
Eric Riz Blog rizinsights
Fabian G Williams Blog fabianwilliams
Fabrice Romelard (Fabrice Romelard) Blog fromelard
Fumio Mizobata (?? ???) Blog
Gustavo Adolfo Velez Duque (Gustavo Velez) Blog
Haaron Gonzalez (Haaron Gonzalez) Blog haarongonzalez
Hans Brender (Hans Brender) Blog HansBrender
Heber Lopes Blog heberolopes
Hemendra Agrawal (Hemendra Agrawal) Blog
Hiroaki Oikawa (?? ??) Blog HiroakiOikawa
Hirofumi Ota Blog hrfmjp
Ivan Padabed (???? ???????) Blog sharepointby
Jake Dan Attis (J. Dan Attis) Blog jdattis
Jasper Oosterveld (Jasper Oosterveld) Blog SharePTJasper
Jennifer Ann Mason Blog jennifermason
JeongWoo Choi Blog
John Liu (John Liu) Blog johnnliu
Joseph Tu (???) Blog
Jovi Ku Blog
Juan Carlos Gonzalez Martin (Juan Carlos González) Blog jcgm1978
Kevin Trelohan (Kevin TRELOHAN) Blog ktrelohan
Laura Derbes Rogers (Laura Rogers) Blog wonderlaura
Mahmoud CHALLOUF Blog
Marat Bakirov (????? ???????) Blog
Mark Stokes Blog MarkStokes
Matthias Einig Blog mattein
Melick Rajee Baranasooriya Blog MelickRajee
Miguel Tabera (Miguel Tabera) Blog migueltabera
Mikael Svenson (Mikael Svenson) Blog
Patrick Lamber Blog patricklamber
Roger Haueter Blog techtask
Romeo Donca (Romeo Donca) Blog romeodonca
Sonja Madsen Blog
Todd C Bleeker (Todd C Bleeker) Blog toddbleeker
Tom Resing Blog resing
Usama Wahab Khan (Usama Wahab Khan) Blog usamawahabkhan
Vielka Rojas Blog vkrojas
Vincent Biret Blog baywet
Wesley Hackett Blog weshackett
Yoshiaki Nishita (?? ??) Blog
Patrick Yong Blog
Jason Himmelstein Blog sharepointlhorn
John P White (John P White) Blog diverdown1964
Jussi Mori Blog JussiMori
Oksana Prostakova (?????? ??????????) Blog prostakova
Pierre Erol GIRAUDY (Erol GIRAUDY) Blog EROL_MVP
Juan Manuel (Manolo) Herrera (Juan Manuel Herrera Ocheita) Blog
Bjoern H Rapp (Bjoern H Rapp) Blog bjoern_rapp
Daniel McPherson (Daniel McPherson) Blog danmc
Devendra Velegandla Blog
Jamil Haddadin (Jamil Haddadin) Blog jamilhaddadin
Noorez Khamis (Rez) Blog nkhamis
Justin Liu (???) Blog FoxdaveJustin
Gavin Barron (Gavin Barron) Blog gavinbarron
Jason Kaczor (Jason Kaczor) Blog jjkaczor
Antonio Maio (Antonio Maio) Blog
Guillaume Meyer (Guillaume Meyer) Blog guillaumemeyer
Jianyu Yang (???) Blog
Nguyen Hoang Nhut (Nguyen Hoang Nhut) Blog nhutcmos
Alan Marshall Blog
Albert-Jan Schot (Albert-Jan Schot) Blog
Amit Vasu Blog
Dinusha Kumarasiri Blog
Doug Hemminger (Doug Hemminger) Blog
Eric Alan Shupps (Eric Shupps) Blog eshupps
Inderjeet Singh Jaggi Blog
Isha Kapoor Blog
Jan Vanek Blog
Jussi Roine Blog jussiroine
Marco Rizzi Blog marcorizzi
Rodrigo Romano (Rodrigo Romano) Blog
Zhonglei Hou (???) Blog
Shereen Qumsieh Blog
Wonbae Kim Blog
Wouter van Vugt (Wouter van Vugt) Blog mobile.twitter.comwoutervugt

 Microsoft Azure:

Name Blog Twitter
Alan Smith Blog
Alexander Feschenko Blog
Alexandre Brisebois Blog Brisebois
Andri Yadi Blog andri_yadi
Andy Cross Blog
Anton Staykov Blog astaykov
Anton Vidishchev Blog
Atsushi Fukui Blog
Aymeric Weinbach Blog zecloud
Benjamin Soulier Blog bsoulier
Bill Wilder Blog codingoutloud
Bruno Kovacic (Bruno Kovacic) Blog
Chris J.T. Auld Blog
Christian Terboven Blog
Ciprian Jichici Blog
Daichi Isami Blog
Daron Yondem Blog daronyondem
David Burela Blog
David Pallmann Blog
David Rendón Blog
David Rodriguez Blog davidjrh
Demetrio U. Tabadero, Jr (June Tabadero) Blog jtabadero
Dennis Burton (Dennis Burton) Blog dburton
Edison Go Tan Blog
Edward Bakker Blog
Eric D Boyd Blog
Fernando de Alcântara Correia Blog
Florent Santin Blog
Gaurav Mantri Blog
Georgiy Mogelashvili Blog glamcoder
Guillaume Belmas Blog
Guy Barrette (Guy Barrette) Blog
Herve Roggero Blog
Ho Kwang Kim (???) Blog
Honggen Li Blog
Hongju Jung Blog
Ibon Landa Blog
Jan Hentschel Blog Horizon_Net
Jason Milgram Blog jmilgram
Jean-Luc Boucho Blog JeanLucBoucho
Jeong Hyun Nam Blog
John Azariah Blog johnazariah
Junao Xue (???) Blog
Kamaludeen Mohamed Faizal Blog
Kazumi Hirose (?? ??) Blog kazumihirose
Keiji KAMEBUCHI Blog kosmosebi
Konstantinos Pantos Blog kpantos
Kuniteru Asami Blog kunyami
Lei Zhang Blog
Lucas Almeida Romão Blog azureservicesbr
Maarten Balliauw Blog maartenballiauw
Magnus Martensson Blog
Mahesh Krishnan Blog MaheshKrishnan
Marc van Eijk Blog _marcvaneijk
Marcondes Josino Alexandre (Marcondes Alexandre) Blog
Mark Rendle Blog markrendle
Michael Corpuz Blog
Michael Washam (Michael Washam) Blog mwashamtx
Michael Wood Blog mikewo
Michael S. Collier Blog michaelcollier
Michel Hubert Blog MichelHubert
Michele Leroux Bustamante Blog michelebusta
Mick Badran Blog
Mickaël MOTTET Blog MCKLMT
Mihai Tataran Blog
Mihail Alexandrov Mateev (?????? ??????????? ??????) Blog
Mike Martin Blog techmike2kx
Ming Chung Chu Blog
Neil Mackenzie Blog mknz
Niraj Bhatt Blog nirajrules
Nuno Filipe Godinho Blog NunoGodinho
Önder Deger Blog onderdeger
Patriek van Dorp Blog pvandorp
Quique Martínez Alén Blog quiqu3
Radu Vunvulea Blog RaduVunvulea
Rainer Stropek Blog rstropek
Richard Astbury Blog richorama
Richard Conway Blog ukwaug?
Rick G. Garibay Blog rickggaribay
Rob Blackwell Blog
Roberto Brunetti Blog
Roberto Freato Blog
Robin Shahan Blog
Rok Berme┼ż Blog Rok_B
Rudy Setyo Purnomo Blog rudysetyo
Ryan Duclos Blog rduclos
Saori Ando Blog
Sascha Dittmann Blog SaschaDittmann
Serena Yeoh Blog
Sergejus Barinovas Blog
Shaun Xu (???) Blog
Simon J Cox Blog
Sky Chang Blog
Steve Spencer Blog sdspencer
Sunao Tomita Blog harutama
Takahito Yamatoya (?????) Blog AzureSQL
Tatsuaki Sakai Blog tatsuakisakai
Teemu Tapanila Blog TapanilaT
Tiberiu Covaci Blog tibor19
Tyler Doerksen Blog tyler_gd
Virgilio Esteves Blog twitter.vraposo.me
Vishal Narayan Saxena Blog
Wade Wegner Blog
Wilfried Woivré Blog wilfriedwoivre
Yaniv Rodenski (Yaniv Rodenski) Blog
Youngjae Kim Blog
Yukinori Kuroda (?? ??) Blog FANKSHPC
Yves Goeleven (Yves Goeleven) Blog
Zia Khan Blog
Zoiner Tejada Blog
Vishwas Lele Blog
Anton Boyko Blog
Atsushi Fukui (???) Blog afukui
Brent Samodien (Brent Samodien) Blog
Dino Wang (???) Blog
Erick Kurniawan Blog
Julien Stroheker Blog Ju_Stroh
Lukasz Kaluzny Blog kaluzaaa
Masaki Yamamoto (?? ??) Blog
Ming Chung Chu (??) Blog
Rik Hepworth Blog rikhepworth
Roland Krummenacher Blog
Sebastiano Galazzo Blog
Shiju Varghese Blog shijucv
Tingli Lv (???) Blog

Office 365

Name Blog Twitter
Alberto Pascual (Alberto Pascual) Blog guruxp
Alexandru Dionisie Blog AlexDionisie
Alvaro dos Santos Rezende Blog
Amin Tavakoli Blog amintvk
Arnaud Blog alcabeza
Ayman Mohammed El-Hattab Blog aymanelhattab
Ben Curry Blog
Benoit HAMET Blog benoit_hamet
Brendon Ford Blog stewartisland
Brett Hill Blog bretthill
Brian Nøhr Blog Bsnohr
Dan Holme Blog danholme
Danny Burlage Blog
Darrell C Webster (Darrell Webster) Blog
Diogo Dias Heringer Blog
Dragan Panjkov Blog panjkov
Eunjoo Lee (Eunjoo Lee) Blog
Fernando Andreazi Blog fandreazi
Genki Watanabe Blog
Gilles Pommier Blog
Goran Husman Blog
HeeJin Lee (Hee Jin Lee) Blog
Igor Pavlekovic Blog igorpnet
Jeremy Dahl Blog
Jesper Ståhle Blog JesperStahle
Jethro Seghers Blog jseghers
JinHwan Woo Blog
Jorge Castañeda Cano Blog xorxe
Kazuhiko Nakamura (?? ??) Blog
Kelsey Epps Blog kelseyepps
Kerstin Rachfahl Blog himmlischeit
Laurent Miltgen-Delinchamp Blog
Loryan Strant Blog thecloudmouth
Malin Dandenell Blog
Mario Cortes Flores Blog
Markus Widl Blog markuswidl
Martina Grom Blog magrom
Masayuki Mokudai (????) Blog Office365Room
Mauricio Cassemiro Blog
Michael Kirst-Neshva Blog ankbs
Myles Jeffery Blog
Naoki Osada Blog
Nitin Sadashiv Paranjape Blog
Nuno Árias Silva (Nuno Árias Silva) Blog NunoAriasSilva
Paul Woods Blog paulwoods
Poo Ching Loong Blog
Raphael Köllner Blog ra_koellner
Rene Dominik Modery Blog modery
Robert D. Crane Blog
Sara Barbosa Blog Sarabarbosa
Sean McNeill Blog s_mcneill
Seiji Noro (?? ??) Blog
Steve Noel Blog CloudItca
Tomislav Bronzin (Tomislav Bronzin) Blog tbronzin
Vincent Choy Blog
Yoni Kirsh Blog
Yoshihide Sakamoto Blog
Zeljka Knezovic Blog zeljkak
Zoltan Zombory (Zombory Zoltán) Blog zomby_z
Peter van Hees Blog mrpetrovic
Alex Pearce Blog
Chris Goosen Blog
Christian Buckley Blog buckleyplanet
David Petree Blog dmixx
Jasper Oosterveld (Jasper Oosterveld) Blog SharePTJasper
Jian Chen (??) Blog loveunicom
Liang Tang (??) Blog
Naohiko Maeda (?? ??) Blog naohikomaeda
Naomi Moneypenny Blog nmoneypenny
Rie Okuda (?? ??) Blog
Robert Dick (Robert Dick) Blog
Sébastien Levert Blog sebastienlevert
WenXing Liao Blog
Yoan Topenot Blog YoanTopenot