Archive for April, 2014

SharePoint Rich Content Editor with CKEditor Widgets

0

In this article, we build a CKEditor widget representing a SharePoint document link. This introduces a SharePoint Rich Content Editor alternative, which provides features to build a variety custom widgets. At the same time, we demonstrate the ability to leverage SharePoint data and the 2013 REST API.

The following image displays a standard CKEditor control, which includes a toolbar containing various features to deliver rich content without requiring the author to understand HTML or CSS.

The first step is create the CKEditor widget, which is the JavaScript plugin.js located in the widget folder. 

 Javascript |  copy code |? 
01
CKEDITOR.plugins.add('documentlinkwidget', {
02
    requires: 'widget',
03
    icons: 'documentlinkwidget',
04
    init: function(editor) {
05
       editor.widgets.add('documentlinkwidget', {
06
           button: 'Add document link',
07
           template:
08
               '<div class="documentlinkwidget" title="">' +
09
               '<a href="" class="doclinkwidget" style="width: 200px;"></a>' +
10
               '</div>',
11
           requiredContent: 'div(documentlinkwidget); a(doclinkwidget)',
12

In the init, we add the widget to the CKEditor. The template defines the widget markup structure. The requiredContent represents the mandatory widget content, which is a div and a element.

 Javascript |  copy code |? 
01
            init: function() {
02
                var width = this.element.getStyle('width');
03
                if (width)
04
                    this.setData('width', width);
05
                else
06
                    this.setData('width', '200px'); //default
07
 
08
                var id = this.element.getAttribute('title');
09
                if (id)
10
                    this.setData('docid', id);
11
            },
12

Next, add an init function to set the data. The width value is retrieved from the div element style attribute. If the style does exist then width is set to “200px” as the default. The id is retrieved from the title attribute, which is the docid value.

 Javascript |  copy code |? 
1
           data: function() {
2
                var width = this.data.width;
3
                if (width == '')
4
                    this.element.removeStyle('width');
5
                else
6
                    this.element.setStyle('width', width);
7

In the data function above, we build the widget markup. The div element style attribute is set to the appropriate width value. Next, we will retrieve the SharePoint document information using a REST API call.

 Javascript |  copy code |? 
01
               var spQuery = {
02
                    element: '',
03
                    url: '',
04
                    init: function(element) {
05
                        spQuery.element = element;
06
                        spQuery.url = _spPageContextInfo.webAbsoluteUrl + "/_api/lists/getByTitle('" + encodeURIComponent(spQuery.element.data.sourcelistname) + "')/items(" + spQuery.element.data.docid + ")/File?$select=Title,Name,Exists,Length,Level,ServerRelativeUrl";
07
                    },
08
                    load: function() {
09
                        $.ajax(
10
                            {
11
                                url: spQuery.url,
12
                                method: "GET",
13
                                headers: { "accept": "application/json;odata=verbose" },
14
                                success: spQuery.onSuccess,
15
                                error: spQuery.onError
16
                            }
17
                        );
18
                    },
19
                    onSuccess: function(data) {
20
                        spQuery.element.element.setAttribute('title', spQuery.element.data.docid);
21
 
22
                        var link = spQuery.element.element.findOne('a');
23
                        if (link) {
24
                            link.setText(data.d.Title || data.d.Name); //title is not required so set to name if missing
25
                            link.setAttribute('href', data.d.ServerRelativeUrl);
26
                            link.setAttribute('class', 'doclinkwidget');
27
 }
28
                    },
29
                   onError: function(err) {
30
                        api.openMsgDialog('', JSON.stringify(err));
31
                    }
32
                };
33
 
34
                //get data if docid is set
35
                if (this.data.docid) {
36
                    spQuery.init(this);
37
                    spQuery.load();
38
                }
39
            }
40

SPQuery contains our initialize and JQuery logic, so we can retrieve the SharePoint document file information to build the widget markup. The init sets the SPQuery.element and SPQuery.url values, which will be used in the load function. The SPQuery.onSuccess and SPQuery.onError are the callbacks to handle the logic based on the response. The link.setText will set the value to Title or Name from the JSON result. The ServerRelativeUrl  result value is the href attribute. You can retrieve the icon and file size, which can also be included in the markup.

The final section will conditionally make the SPQuery call when a docid value is present. This is accomplished with SPQuery.init(this) and SPQuery.load() calls.

We also add logic to support a widget configuration dialog, which  allows the user to select a document and set the width. The following is the additional plugin.js code required to define a dialog.

 Javascript |  copy code |? 
1
init: function(editor) {
2
        CKEDITOR.dialog.add('documentlinkwidget', this.path + 'dialogs/documentlinkwidget.js');
3
 
4

We will cover the CKEditor widget dialog in the next post. The following is the complete widget plugin.js.

 Javascript |  copy code |? 
01
CKEDITOR.plugins.add('documentlinkwidget', {
02
    requires: 'widget',
03
 
04
    icons: 'documentlinkwidget',
05
 
06
    init: function(editor) {
07
        CKEDITOR.dialog.add('documentlinkwidget', this.path + 'dialogs/documentlinkwidget.js');
08
 
09
        editor.widgets.add('documentlinkwidget', {
10
            button: 'Add document link',
11
 
12
            template:
13
                '<div class="documentlinkwidget" title="">' +
14
                '<a href="" class="doclinkwidget" style="width: 200px;"></a>' +
15
                '</div>',
16
 
17
            allowedContent:
18
                'div(!documentlinkwidget); a(!doclinkwidget)',
19
 
20
            requiredContent: 'div(documentlinkwidget); a(doclinkwidget)',
21
 
22
            dialog: 'documentlinkwidget',
23
 
24
            upcast: function(element) {
25
                return element.name == 'div' && element.hasClass('documentlinkwidget');
26
            },
27
 
28
            init: function() {
29
                var width = this.element.getStyle('width');
30
 
31
                if (width)
32
                    this.setData('width', width);
33
                else
34
                    this.setData('width', '200px'); //default
35
 
36
                var id = this.element.getAttribute('title');
37
                if (id)
38
                    this.setData('docid', id);
39
            },
40
 
41
            data: function() {
42
                var width = this.data.width;
43
 
44
                if (width == '')
45
                    this.element.removeStyle('width');
46
                else
47
                    this.element.setStyle('width', width);
48
 
49
                var SPQuery = {
50
                    element: '',
51
                    url: '',
52
                    init: function(element) {
53
                        SPQuery.element = element;
54
                        SPQuery.url = _spPageContextInfo.webAbsoluteUrl + "/_api/lists/getByTitle('" + encodeURIComponent(SPQuery.element.data.sourcelistname) + "')/items(" + SPQuery.element.data.docid + ")/File?$select=Title,Name,Exists,Length,Level,ServerRelativeUrl";
55
                    },
56
                    load: function() {
57
                        $.ajax(
58
                            {
59
                                url: SPQuery.url,
60
                                method: "GET",
61
                                headers: { "accept": "application/json;odata=verbose" },
62
                                success: SPQuery.onSuccess,
63
                                error: SPQuery.onError
64
                            }
65
                        );
66
                    },
67
                    onSuccess: function(data) {
68
                        SPQuery.element.element.setAttribute('title', SPQuery.element.data.docid);
69
                        var link = SPQuery.element.element.findOne('a');
70
                        if (link) {
71
                            link.setText(data.d.Title || data.d.Name); //title is not required so set to name if missing
72
                            link.setAttribute('href', data.d.ServerRelativeUrl);
73
                            link.setAttribute('class','doclinkwidget');
74
                        }
75
                    },
76
                   onError: function(err) {
77
                        api.openMsgDialog('', JSON.stringify(err));
78
                    }
79
                };
80
 
81
                //get data if docid is set
82
                if (this.data.docid) {
83
                    SPQuery.init(this);
84
                    SPQuery.load();
85
                }
86
            }
87
        });
88
    }
89
});
90
 
91

TFS Work Item Templates Filtering User Selector

0

If you are managing multiple Team Foundation Server (TFS) project teams then you probably would prefer to filter the user/people selector based on project or roles. The standard list includes all users, which contains more users than necessary.

Visual Studio provides an option to modify the work item templates. You can access this option at Tools-Process Editor-Work Item Types-Open WIT From Server. Select the Team Foundation Server and Project Collection before clicking Connect. The available work item templates will appear. Next, select the appropriate template to apply the filter. In this example, the User Story was selected.

 On the Fields tab, select the Assigned To field.

After selecting the field, the Fields Definition will be presented.

Click the Rules tab, which provides the filtering options for the Assigned To field.

Select ALLOWEDVALUES and click Edit. You can assign a project group with the [project] placeholder and the name. In my case, assigning the TFS project team.

Once you save the work item template, visiting a User Story and selecting the Assigned To field will display the filtered user list.

You can also export the work item template and add to TFS source control. In this case, modifying the following XML will also update the filter.

 XML |  copy code |? 
1
<FieldDefinition name="Assigned To" refname="System.AssignedTo" type="String" syncnamechanges="true" reportable="dimension">
2
  <ALLOWEXISTINGVALUE />
3
  <ALLOWEDVALUES expanditems="true" filteritems="excludegroups">
4
    <LISTITEM value="[project]\My Team" />
5
  </ALLOWEDVALUES>
6
  <HELPTEXT>The person currently working on this story</HELPTEXT>
7
</FieldDefinition>
8

Hopefully, this tip can save you time applying a filter to a user/people selector.

Go to Top