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