Showing posts with label Apps. Show all posts
Showing posts with label Apps. Show all posts

Thursday, April 24, 2014

SharePoint 2013: Require JS + SharePoint Hosted Apps


During my early days I was bit scared writing JavaScript applications since I was so use to C# that I always try to find similar feature or offerings in JavaScript, I must say it took me sometime to really grasp the concepts because there are different ways to do a single task e.g. creating an object. I wanted to apply the basic principles of SOLID and found myself nowhere near to adhering to it.

SOLID:

Single responsibility (SRP)

Open Close (OCP)

Liskov substitution principle (LSP)

Interface segregation principle (ISP)

Dependency inversion principle (DIP)


 

Well I'm too young in JavaScript still to apply all the principles but have at-least I now able to partially apply SRP, DIP up to a certain extent.
Mostly I've seen in SharePoint Hosted apps developers writing a big fat JavaScript file which contains all the business logic and it becomes hard to read and debug (really I mean it).
Modular programming helps a lot here but to achieve one must need to understand modules (class) and how they can be defined in JavaScript, splitting your logic in modules certainly helps you to achieve SRP but that still doesn't solves the root problem as you are still loading the entire module at once which doesn't gives you much benefit in terms of performance. Well loading individual modules can be loaded but you need a solution, and the answer to that is Require.JS, it's an AMD (Asynchronous Module Definition implementation)
Well if you are SharePoint developer then you'll argue that why we need another framework for loading individual module which is already provided by SharePoint via SP.SOD (Script on Demand).
A quick comparison between both approaches
Require JSSOD (Script On Demand)
Highly ConfigurableNo Configurations
Support to External LibrariesNo Exports for External Libraries
Inject, Exports, ShimNo exports or injection support (natively loads the plugin)
Cleaner Dependency construct and resolutionComplex construct to write dependencies within external libraries

 

I won't go in detail of how to setup require.js the documentation is itself self-explanatory trust me It took me a while to get this up but now it's a piece of cake !!!
I'll talk about some important part of the configuration which is required so that a SharePoint developer can leverage functionality and get benefitted from best of both worlds
The key aspect in require.js is configuration which is very powerful important attribute

 



 

Above is my simple configuration for require.js 

Please focus on deps and paths property

#1 Paths : The SharePoint JSOM runtime and main js to be loaded.
#2 Deps : Deps setting causes the runtime and sp.js file to be loaded before any module is loaded, thus making required core and necessary classes available for developer to program.

 

The Module

I've created two module which fetches the current user display name from web and user profile

// Module for Web



 


 

// Module for User Profile

User Profile Code : http://www.vrdmn.com/2013/02/sharepoint-2013-working-with-user.html @vrdmn

 



 


 

The Start-up Module




SP.SOD.loadMultiple(['sp.js', 'userprofile'], ShowUserName);

#1 The responsibility of ensuring that sp.js and sp.userprofile.js has been loaded is delegated to native SharePoint SOD module, the trick is to use SP.SOD.loadMultiple() which loads multiple modules and triggers the final function to be loaded.
Note: Here you can see the difference in the way require.js works and SOD works, require.js injects the dependencies as a parameters and SOD doesn't which gives more clear understanding of what to expect.


I've already published the sample at github: https://github.com/akhileshnirapure/sp_hosted_app_requirejs

Feel free to fork and if you have any question the reach me at Twitter: @AkhileshN
Thanks
Akhilesh Nirapure


Saturday, February 1, 2014

SharePoint 2013 Client Side People Picker + SharePoint Hosted Apps + Knockout

It’s been quite a while playing with knockout, and trust me a great learning, every framework especially JavaScript has a stiff learning curve so as with knockout. Working on SharePoint Hosted App came across a valid use case of having a people picker to be part of application, integrating it using jQuery and as per Microsoft’s guideline it’s a cake walk (here). Well as I mentioned using with knockout was a bit of challenge, dealing with some really simple requirement can be scary sometime. So let’s look at it.

Requirement: User should be able to lookup user and also the control should be pre-populated initially if data exists.

Well I’m not going to go in details every bits of the code, I assume you already have some idea about how knockout works if not then a good starting point knockoutjs.com.

Okay enough talk, in a nut shell what you need is a custom binding handler to work with.

Before we get started, just a quick note, there is no one-size which fits all so the code which I’m going to show you will not suffice all the requirement, so my suggestion get inspiration and tweak as you need J

ko.bindingHandlers.kopeoplepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {

var schema = {};
schema['PrincipalAccountType'] = 'User';
schema['SearchPrincipalSource'] = 15;
schema['ShowUserPresence'] = true;
schema['ResolvePrincipalSource'] = 15;
schema['AllowEmailAddresses'] = true;
schema['AllowMultipleValues'] = false;
schema['MaximumEntitySuggestions'] = 50;
schema['Width'] = '280px';
schema["OnUserResolvedClientScript"] = function (elemId, userKeys) {
// get reference of People Picker Control
var pickerElement = SPClientPeoplePicker.SPClientPeoplePickerDict[elemId];
var observable = valueAccessor();
observable(pickerElement.GetControlValueAsJSObject()[0]);
console.log(JSON.stringify(pickerElement.GetControlValueAsJSObject()[0]));
};


// TODO: You can provide schema settings as options
var mergedOptions = allBindingsAccessor().options || schema;

// Initialize the Control, MS enforces to pass the Element ID hence we need to provide
// ID to our element, no other options
this.SPClientPeoplePicker_InitStandaloneControlWrapper(element.id, null, mergedOptions);

// Force to Ensure User
var userValue = ko.utils.unwrapObservable(valueAccessor());
var pickerControl = SPClientPeoplePicker.SPClientPeoplePickerDict[element.id + "_TopSpan"];
var editId = "#" + pickerControl.EditorElementId;
jQuery(editId).val(userValue);

// Resolve the User
pickerControl.AddUnresolvedUserFromEditor(true);


}
};



Microsoft’s Client Side People picker relies heavily on ID of element for the implementation of its controls (editor, hidden fields, etc.) so we have to work with it in our custom binding handler I wanted to highlight this point because most of the examples you see with KO is able to work with the element property itself)


#1 here we are initializing the People Picker Control.


 


#2 here we are looking for People Picker control using a special suffix which helps us in grabbing the control and then finding out the Editor element id (which actually has the value) and then making use of AddUnresolvedUserFromEditor(true) to resolve the user passed in a value. E.g. akhilesh.nirapure@zevenseas.com.


 


#3 here I’m providing my own implementation of what to do when we have a resolved user (from server) by using “OnUserResolvedClientScript” event. Again we here need to make use of People Picker control (we have to again find out by its id) and make use of GetControlValueAsJSObject() method which gets us the result as JSON.


 


Well the above implementation is not full blown code with exceptional handling it can be used as a reference point of how we can work with various event.


 


I’ve uploaded my sample code on GitHub (https://github.com/akhileshnirapure/sp13peoplepickerko) please feel free to fork it and extend it as you want.


 


Akhilesh Nirapure (@AkhileshN)

Monday, April 1, 2013

SharePoint Hosted App, App Part, Document Library Upload.aspx / EditForm.aspx and “X-Frame-Options”

 

Before we drill and talk further would like to take sometime to explain different pieces in the topic. Lets first look at X-Frame-Options.

X-Frame-Options : In a simplest way this is a response header which tells browser (client) that the Website doesn’t want its page (which is currently being accessed) to be shown in a IFrame so as to prevent Clickjacking (Wikipedia : http://en.wikipedia.org/wiki/Clickjacking).

So how do X-Frame-Options related to SharePoint App’s. Well every app developer in SharePoint 2013 is now familiar with the App Model Concept, it has its own App Web and isolated domain which is different then Hosting Web.

To enhance security in SharePoint 2013 by default the “X-Frame-Options” header is sent back with response If you open up SPRequestModule in your fav decompiler you’ll find below statement written in PreSendRequestHeaders method, which gets added to every response if the AllowFraming Flag is not found in the current context items collection.

   1:  if (!httpContext.Items.Contains(SPRequestModule.AllowFramingFlag) && SPRequestModule.ContextCompatibilityLevel != 14)
   2:                         
   3:   {
   4:                              
   5:     httpContext.Response.AddHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
   6:                          
   7:   }

Wait a minute what does that mean, so can we override this and make these line skip for those pages which explicitly marks and request for exception? yes this can be done simply by adding AllowFraming WebPart on to the page which you want to be able to be accessed from within IFrame.


"AllowFraming” Webpart does nothing but adds a simple statement in on load.


protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    if (this.Context != null && !this.Context.Items.Contains(SPRequestModule.AllowFramingFlag))
    {
        this.Context.Items.Add(SPRequestModule.AllowFramingFlag, "1");
    }
    if (this.Visible)
    {
        this.Visible = false;
    }
}



Consider a requirement, you have to upload a document to Document library which is in App, and this App is hosted in an App Part on some page, sound pretty simple.


Just as expected you’ll just point to upload.aspx page of App Document Library which will look something like http://your-fancy-appurl/SomeApp/Lists/Documents/Forms/Upload.aspx and using SP.UI.ModalDialog.showModalDialog you can open the page Upload page in SharePoint dialog box, but you’ll be presented with  annoying error message that you cannot do that it’s because of X-Frame-Options header instructed the browser to do so.


Load denied by X-Frame-Options: http://your-fancy-app-url/_layouts/15/Upload.aspx?List={guid}&IsDlg=1 does not permit cross-origin framing.




Take a pause and you’ll be thinking is where did that _layout/15/Upload.aspx page came in picture (this I’ll explain in later post) and what it has to do with X-Frame-Options.


Investigating further, I opened Upload.aspx under layouts folder and had a look if it has got AllowFraming webpart added to it or not, and i couldn’t locate so i added that entry manually and again gave a try and voila it worked !!!! because now the SPRequestModule –> X-Frame-Options won’t get executed. Next moving further after uploading document i was redirected to EditForm.aspx and i was again shown the same error (highlighted above) but this time instead of Upload.aspx it was EditForm.aspx, so where do you find this now, its actually stored in DataBase and not in layouts which you can just go and make an entry and make it work.


Hold you horses dude, get back to basics, how does this EditForm.aspx gets created? If you look at the schema.xml file of document library the below section where it is all written


<Forms>
      <Form Type="DisplayForm" SetupPath="pages\form.aspx" Url="Forms/DispForm.aspx" WebPartZoneID="Main" />
      <Form Type="EditForm" SetupPath="pages\form.aspx" Url="Forms/EditForm.aspx" WebPartZoneID="Main" />
      <Form Type="NewForm" Url="Forms/Upload.aspx" WebPartZoneID="Main" />
</Forms>



These forms are generated based on forms.aspx a template page which is in pages folder under 15/Template. Well now i again had a look to form.aspx and i again couldn’t locate the AllowFraming webpart, so i added one there again. So in order to regenerate my EditForm.aspx from the same form.aspx i need to re-deploy my app and now everything worked in my App in an App Part.


But is it a good practice No !!!  Ninjayou shouldn’t be modifying Baring teeth smilethese pages to make these solution work, so what’s next


A more elegant approach would be to use HttpModule and trap BeginRequest Event and make it work.


   1:      public class ZSFramingModule : IHttpModule
   2:      {
   3:          public void Dispose()
   4:          {
   5:          }
   6:   
   7:          public void Init(HttpApplication context)
   8:          {
   9:             context.BeginRequest += OnBeginRequest;
  10:              
  11:          }
  12:   
  13:          private void OnBeginRequest(object sender, EventArgs e)
  14:          {
  15:              HttpApplication l_application = (HttpApplication)sender;
  16:   
  17:              if (l_application.Request.Url.ToString().ToLower().Contains(@"/_layouts/15/upload.aspx")
  18:                                                  ||
  19:                  l_application.Request.Url.ToString().ToLower().Contains(@"editform.aspx"))
  20:              {
  21:                  HttpContext.Current.Items.Add("AllowFraming", "1");
  22:              }
  23:   
  24:          }
  25:      }



Hope this helps someone Smile 


 


Happy SharePointing !!!