Blog

Mar 22
Please expose msls.sharepoint, please.

Interacting with SharePoint from javascript in HTML Client requires loading the SP CSOM scripts, referring to the AppWebUrl and HostWebUrl, and for HostWeb Calls - the cross-site request executor is needed.

All these operations are happening in msls.js and are nicely built into the msls.sharepoint object, however that object is contained in an anonymous self-executing function and it is not exposed via msls.expose().

Therefore the msls.sharepoint object cannot be used in custom code at runtime without altering msls.js

Like so:

    msls_sharepoint = {
        hostUrl: hostUrl,
        appWebUrl: appWebUrl,
        serverUrl: serverUrl,
        chromeBackgroundColor: chromeBackgroundColor,
        chromeLinkFontColor: chromeLinkFontColor,
        sharePointImagesUrl: sharePointImagesUrl,
        context: null,
        hostWeb: null,
        appWeb: null,
        executor: null,
        ready: promise.then,
        process: function () {
            var me = this, context = me.context, deferred;
            if (!queryPromise) {
                deferred = $.Deferred();
                queryPromise = deferred.promise();
                msls_dispatch(function () {
                    context.executeQueryAsync(
                        function () { deferred.resolve(); },
                        function (error) { deferred.reject(error); }
                    );
                    queryPromise = null;
                });
            }
            return queryPromise;
        },
        load: function () {
            var me = this, context = me.context, deferred = $.Deferred(),
                args = Array.prototype.slice.call(arguments, 0);
            args.forEach(function (o) {
                context.load(o);
            });
            return me.process();
        },
        openDocument: openDocument,
        getFileIconUrl: getFileIconUrl,
        getDefaultFileIcon: getDefaultFileIcon,
        getFileExtension: getFileExtension
    };
    //Custom: expose msls.sharepoint
    msls_expose('sharepoint',msls_sharepoint);

 

If it were exposed, then we wouldn't have to duplicate the above requirements when do scenarios such as:

Create or alter lists; Check for existence of an item; upload documents; check group membership; query items in order to... let's say, populate a choice list; anything else that requires a call to sharepoint.

CBA is THE RAD tool for sharepoint apps yet many LS devs haven't yet fully dug into the SP REST API.  It only makes sense to expose the msls.sharepoint REST executor so we don't have to deal with the requirements - we can just post a request using the familiar promise operation.

Another benefit is, if the object were exposed, we could extend it.  Devs could build all sorts of useful new reusable methods and append them to the msls.sharepoint object while avoiding any changes to msls.js. 

Buy the way, this goes for any of the msls objects and methods.  For example, anyone want to create controls in the msls.ui.controls namespace?  Can't - it's not exposed.

Discuss this topic here:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/69b99f26-d59c-49c9-ad92-835cd8c57aea/please-expose-mslssharepoint-please?forum=lightswitch#1fe0528d-0842-45ed-a07d-40a8f832433a

Please vote for this suggestion on UserVoice here:

LightSwitch HTML Client (CBA) - Please expose msls.sharepoint object

Have a great day! 

Mar 18
HTML Client Upload Control

The March '14 Update added some nice features including a control to upload documents into a SharePoint Library.  It's really handy as long as your scenerio is exactly what it was desinged to handle and nothing less.  The control can be added to a LightSwitch screen containg a document library by choosing the createOrUploadDocument method for a command bar button.  Unfortuanately, the control only works if the document library is related to an Entity via a virtual relationship (Navagation Property) in LightSwitch.

This means:

1 )  it cannot be used if you simply throw a doc list on a browse screen and try to upload to the library and

2)  it cannot be used if your app has a SharePoint list as the parent entity in the relationship. 

The control checks for a navagation property then, if one is not found, it pukes "The Control cannot be used in this context'  error message and quits.

There are two ways to fix this:

1) Prevent the control from requiring a relationship and/or

2) Force the relationship to exist when Sharepoint List is the parent entity

Neither is 'supported' by MSFT.  To prevent the control from requiring a relationship you have to alter msls-2.5.0.js which I don't prefer, but I'll show you here:

        function setDocumentRelationship(control, itemId) {
        var deferred = $.Deferred();

        control._entitySet.filter("Id eq " + itemId)
            .merge(msls.MergeOption.unchangedOnly)
            .execute()
            .then(function onDocumentEntityRequestSuccess(response) {
                var documentEntity = response.results[0],
                    contentItem = control.data,
                    visualCollection = contentItem.value,
                    loader = visualCollection._loader,
                    collectionProperty,
                    data,
                    propertyValue,
                    propertyName;
                //custom: removed assignment of 4 vars above
                //custom: added if block with assignments below
                if (loader && loader._collectionProperty) {
                    collectionProperty = loader._collectionProperty,
                    data = collectionProperty._entry.data,
                    propertyValue = collectionProperty.owner,
                    propertyName = data.toPropertyName;
                }
                //custom: 'propertyName &&' added to condition below
                if (propertyName && documentEntity[propertyName] !== propertyValue) {
                    documentEntity[propertyName] = propertyValue;

                    window.msls.application.applyChanges()
                        .then(deferred.resolve, deferred.reject);
                } else {
                    visualCollection.refresh()
                        .then(deferred.resolve, deferred.reject);
                }
            }, deferred.reject);

        return deferred.promise();
    }

 Also edit this function:

      function _attachViewCore(templateData) {
        var me = this,
            view = me.getView(),
            entitySetModel,
            docLibNameAttribute,
            contentItem = me.data,
            valueModel = contentItem.valueModel,
            entityTypeModel,
            application, entityType, dataWorkspace;

        me._loaded = false;
        me._loadingElement = msls_getTemplateItem(view, templateData.loadingElementPath);
        me._menuContainerElement = msls_getTemplateItem(view, templateData.menuContainerElementPath);
        me._createDefaultListElement = msls_getTemplateItem(view, templateData.createDefaultListElementPath);
        me._titleContainerElement = msls_getTemplateItem(view, templateData.titleContainerElementPath);
        showSpinner(me);
        me._menuItemsRing = [];
        me._contentTypes = [];
        me._lastPopupPosition = null;

        //custom: below block remmed
        //if (!valueModel.query || !valueModel.query.source.links) {
        //    showError(me, msls_getResourceString("createOrUploadDocument_noNavigationPropertyError"));
        //    return;
        //}

        if (!msls_sharepoint) {
            showError(me, msls_getResourceString("createOrUploadDocument_siteError"));
            return;
        }

        msls_sharepoint.ready(function onSharePointLibrariesReady() {
            if (!msls_sharepoint.hostWeb) {
                showError(me, msls_getResourceString("createOrUploadDocument_siteError"));
                return;
            }

            me._canCreateDocuments = isCreateDocumentAvailable();

            application = window.msls.application;
            entityTypeModel = valueModel.elementType;
            entityType = application[entityTypeModel.name];
            dataWorkspace = application.activeDataWorkspace;
            me._entitySet = msls_EntitySet_getEntitySetForEntityType(dataWorkspace, entityType);
            entitySetModel = me._entitySet.getModel();
            docLibNameAttribute = msls_getAttribute(entitySetModel, msls_sharePointListModelAttribute);

            if (!docLibNameAttribute) {
                showError(me, msls_getResourceString("createOrUploadDocument_entityChangeError"));
                return;
            }
            me._docLibName = docLibNameAttribute.title;

            if (!me._canCreateDocuments) {
                me._createDefaultListElement.attr("style", "display: none;");
                me._titleContainerElement.attr("style", "display: none;");
            } else {
                attachCreateMenuItemsToView(me);
            }

            getDocumentLibraryInfo(me, true)
                .then(function addAllMenuItems() {
                    attachControlToView(me, templateData);

                    hideSpinner(me);
                    me._loaded = true;
                    repositionControl(me);
                }, function onGetDocumentLibraryInfoError() {
                    showError(me, msls_getResourceString("createOrUploadDocument_getListError"));
                })
                .always(function () {
                    msls_notify(loadedNotification, me);
                });
        }, function onSharePointLibrariesError() {
            showError(me, msls_getResourceString("createOrUploadDocument_siteError"));
        });
    }
   

To force the relationship to exist you can edit the entity .lsml file for the doc library like this:

 Related Sharepoint Lists Missing Navigation Properties - BUG?

More discussion here:

 Integrating document libraries in March 2014 with SP list as parent entity

Mar 12
Monkey Patched LightSwitch Screen Collection Queries

This code overrides the queries of all collections on the screen to inject a filter and sort. The solution combines a trick to override the default query of a screen collection, iterating over screen collections and introduces a Monkey Patch to modify the runtime code without altering the original source.

 

myapp.BrowseDocsByContact.created = function (screen) {
    // Write code here.
    //TO DO: test another way to get collections with less code 
    var model, collectionProperties, collectionName, property;
    model = screen.details.getModel();
    collectionProperties = msls.iterate(model.properties)
                    .where(function (p) {
                        return p.propertyType.kind == "CollectionView";
                    })
                    .array;
    collectionProperties.forEach(function (collectionValue, index) {
        collectionName = collectionProperties[index].name;
        property = screen.details.properties[collectionName];
        query = property._entry.simpleDescriptor.createQuery;
        //only overide once - have we done this already?
        if (!query.old) {
            //save the old query
            property._entry.simpleDescriptor.createQuery.old = query;
            //override the query
            property._entry.simpleDescriptor.createQuery =
                //TO DO: can we make these argument generic
                function (ContactId, filterString, sortString) {
                    //if these params don't exist in the query or both have no value
                    if (!filterString && !sortString) {
                        //do the default behavior
                        return query.old.apply(this, arguments);
                    } else {
                        // append filter and orderBy methods
                        return query.old.apply(this, arguments)
                            .filter(filterString)
                            .orderBy(sortString);
                    }
                };
        }
    });
};
Mar 11
LightSwitch HTML Client Collection Query Override

Queries in LightSwitch are implemented as functions in the OData DataService. For this reason overriding the .createQuery method on a screen collection is not as simple as seen in my previous post here:

 LightSwitch HTML Client Filter Hack

It is possible if you find the generated code in data.js and append the .filter and .orderBy methods to the returned DataServiceQuery object.

 

 myapp.BrowseCities.created = function (screen) {
    // Write code here.
    screen.details.properties["Cities"]._entry.simpleDescriptor.createQuery =
        function (filterString, sortString) {
            return new $DataServiceQuery({ _entitySet: this.Cities },
                                    lightSwitchApplication.rootUri + "/ApplicationData.svc" + "/CitiesByState()",
                                    {
                                        StateId: $toODataString(StateId, "Int32?"),
                                        filterString: $toODataString(filterString, "String?"),
                                        sortString: $toODataString(sortString, "String?")
                                    }).filter(filterString).orderBy(sortString);

        };
};

 

Mar 04
SharePoint Forms and LightSwitch

 

Couldn't make the SharePoint Conference, but I was a lurker in the session Update in InfoPath and SharePoint Forms thanks to a twitter feed by @jesscollicott.

The big take aways are:

  • The current roadmap has a focus on Information Workers and Developers are not discussed
  • Much of it is yet to be determined
  • MSFT want your feedback here: http://officeforms.uservoice.com

     

It was no surprise that Access took the prize for winner in category: 'App Forms Over Relational Data', but I must say it was a bit of a letdown.

Here are a couple slides from the session deck.

Having been an Access dev for over 16 years, I feels strange it's a letdown. I suppose the fact that all those years doing RAD over relational data while feeling like a second class citizen to two other classes of MSFT stakeholders drove me away from Access toward LightSwitch. Who are these stakeholders? 'Information Workers' (read: MS Office licensees) and 'Real Developers' (read: Visual Studio licensees). So I've moved into the Visual Studio crowd and learned a lot about a great RAD tool called LightSwitch which has been exciting.

Fast forward a couple years and here we are, Access gets first billing on SharePoint Forms over relational data and LightSwitch – nothing. I am happy for Access, but remain disappointed in how product roadmaps are envisioned at MSFT - everything is framed in terms of IW vs. DEV.

What now for LightSwitch? Let's hope for an upsize path from Access Web Apps to LightSwitch Cloud Business Apps.

You can make your voice heard here:

Roadmap needs: Upsize Wizard from Access Web App to LightSwitch

Collaborate with the LightSwitch team and enhance both products - LightSwitch is the right solution for vNext

Browser design basic --> Visual Studio LightSwitch advanced

Feb 27
Iterate over Screen Collections

This post is a work in progress …

    var model, collectionProperties, collectionName, property;
    model = screen.details.getModel();
    collectionProperties = msls.iterate(model.properties)
                    .where(function (p) {
                        return p.propertyType.kind == "CollectionView";
                    })
                    .array;
    collectionProperties.forEach(function (collectionValue, index) {
        collectionName = collectionProperties[index].name;
        property = screen.details.properties[collectionName];
        property._entry.simpleDescriptor.createQuery =
            function (filterString, sortString) {
                return this.dataWorkspace.ApplicationData[collectionName]
                    .filter(filterString)
                    .orderBy(sortString);
            };
    });
};

 

 
myapp.BrowseDocsByContact.created = function (screen) {
    // Write code here.
    var model, collectionProperties, collectionName, property;
    model = screen.details.getModel();
    collectionProperties = msls.iterate(model.properties)
                    .where(function (p) {
                        return p.propertyType.kind == "CollectionView";
                    })
                    .array;
    collectionProperties.forEach(function (collectionValue, index) {
        collectionName = collectionProperties[index].name;
        property = screen.details.properties[collectionName];
        query = property._entry.simpleDescriptor.createQuery;
        if (!query.old) {
            property._entry.simpleDescriptor.createQuery.old = query;
            property._entry.simpleDescriptor.createQuery =
                function (ContactId, filterString, sortString) {
                    if (!filterString && !sortString) {
                        //do the default behavior
                        return query.old.apply(this, arguments);
                    } else {
                        // append filter and orderBy methods
                        return query.old.apply(this, arguments)
                            .filter(filterString)
                            .orderBy(sortString);
                    }
                };
        }
    });
};
Feb 26
LightSwitch HTML Client Filter Hack

Discovered something today in HTML Client that allows us to forget about Linq Dynamic Query Library and PreProcessQuery completely.

If you overwrite the createQuery method of the screen collection property's simpleDescriptor object, you can inject an OData filter string.

myapp.BrowseCities.created = function (screen) { 
    // Write code here.
    screen.details.properties["Cities"]._entry.simpleDescriptor.createQuery = 
        function (filterString) { 
            return this.dataWorkspace.ApplicationData["Cities"]
            .filter(filterString); 
        }; 
};
   

In addition to the code above, all you do is add an optional parameter to the screen query (no filter, and no PreProcessQuery code needed)

 

 

Simply drag the parameter onto the screen designer to make a screen property and change the control to text box.

 

 

Now F5 and type some OData filter string (see this: http://www.odata.org/documentation/odata-version-3-0/url-conventions ) and voila!

 

 

Surely, you can imagine how this could be useful, Sherley.

Feb 02
This blog

Mostly for my own reference, but something here might be useful, enjoy.

SharePoint Help  
Looking for recommendations...

How to

There is no recommended article for this page. Try searching help and community.
This shouldn't take long.

Community

Can't find what you are looking for? Post your question to the Office 365 Community
Sorry, we couldn't find an answer. Post your question to the Office 365 Community
Sorry, the help service is down, but you can find answers at Office support