Copy current Access database with VBA

Access is still being in used by some companies, even is not perfect choice anymore. So, today I was requested to create a sort of back-up functionality for an Access database which contains linked tables to SharePoint. I have tried some methods, but all them are failed, including FileCopy method from VBA. Still VBA was solving my problem, but code was a little bit different.

Dim fDialog As Office.FileDialog
Set fDialog = Application.FileDialog(msoFileDialogSaveAs)

With fDialog

    If fDialog.Show() <> 0 Then
    
        Dim Sourcefile As String
        Dim Destinationfile As String
        Sourcefile = Application.CurrentProject.Path & "\" & Application.CurrentProject.Name
        Destinationfile = fDialog.SelectedItems(1)
    
        With CreateObject("Scripting.FileSystemObject")
            .copyfile Sourcefile, Destinationfile
        End With
       
      
    End If
End With

Code is also opening a file save dialog and you can choose the location where you want to copy the database.

Advertisements

CDN issues in SharePoint apps

When I am developing SharePoint apps, in general, I create my custom master page and place it inside the application. I would not say I had too much problems with this approach, until I needed to deploy the application on a SharePoint Online site where I could not disabled CDN due to insufficient rights on tentant. JQuery loaded from CDN simply makes some Bootstrap functionality not to work anymore.

My scripts were loaded using ScriptManager control, which is the classic way to load javascript files in web forms sites. As SharePoint tried to load additional files, the salvation was again JavaScript.


function pageLoad(sender, args) {



    let scripts = document.querySelectorAll('script')
    for (let k = 0; k < scripts.length; k++) {
        if (scripts[k].src.includes('ajax.aspnetcdn.com/ajax/jquery') === true) {
            document.body.removeChild(scripts[k]);
            jQuery.getScript(_spPageContextInfo.webAbsoluteUrl + '/Scripts/Framework/jQuery/jquery.js', function () {
                jQuery.getScript(_spPageContextInfo.webAbsoluteUrl + '/Scripts/Framework/bootstrap/bootstrap.js', function () {
                    // Do something here if needed
                });
            });
        }
    }

}

Don’t judge me if the code is not optimized as I found the solution about 10 minutes ago and this article is only written to give you an idea about how to remove jQuery from CDN. You can remove of course old Bootstrap and jQuery files before adding new ones or try to find a better loader, like require.js.

Adding image in master page (SharePoint apps model)

When I developing SharePoint apps, I prefer to use my custom master page, as the idea is to create responsive design. One of the issues I had was to how to add a logo or an image in the master page. The solution is still classic asp.net control called image.

<asp:Image runat="server" ImageUrl="../../Images/sp_logo.png" />

You can apply style on the image by setting CssClass property of the control, as well as you can use all other properties. See full documentation for this tag here: http:// https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.image?view=netframework-4.7.2

Create folder with properties using JavaScript CSOM

JavaScript CSOM is already on the market for a while. Introduces on version 2010, it can cover today a lot of scenarios. One of them is to create a folder in a document library with some other associated fields populated. This was one of my recent tasks. Code below should to do everything for you:

Type.registerNamespace('Shp');
        
        
        Shp.DocLib = function() {
            throw 'Cannot initiate Shp.DocLib static class';        
        }
        
        Shp.DocLib.createFolder = function(listName, folderName, fields, webUrl, success, fail) {
            var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'folderName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'fields', type: Object, optional: false, mayBeNull: false },
                                                            { name: 'webUrl', optional: false, mayBeNull: true },
                                                            { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                            { name: 'fail', type: Function, optional: true, mayBeNull: false }], true);
           if(e) throw e;

   		  var fail = fail || function (err) { alert(err); };
          var ctx = (webUrl === null) ? SP.ClientContext.get_current() : new SP.ClientContext(webUrl);          
          Shp.DocLib._createFolder(listName, folderName, fields, ctx, success, fail);
        }
        
        Shp.DocLib._createFolder = function(listName, folderName, fields, ctx, success, fail) {
        
        	var oLib = ctx.get_web().get_lists().getByTitle(listName); 
        	var itemCreateInfo = new SP.ListItemCreationInformation();
        	itemCreateInfo.set_underlyingObjectType(SP.FileSystemObjectType.folder); 
        	itemCreateInfo.set_leafName(folderName);
        	var oFolder = oLib.addItem(itemCreateInfo);
        	for (var field in fields) {
                if (fields.hasOwnProperty(field) === true) {
                    oFolder.set_item(field, fields[field]);
                }
            };
            
     		oFolder.update();
            ctx.load(oFolder);     
            
			ctx.executeQueryAsync(function () {
                success(oFolder);
            }, function (sender, args) {
                fail(args.get_message());

            });			  
        }
        
        Shp.DocLib.registerClass('Shp.DocLib');

In order to use, you just need to provide right types of parameters, as a validation is performed.

Shp.DocLib.createFolder("List Name", "Folder Name", { "Field1": "some value", "Field2": "some value" }, "path to sharepoint",
    function (folder) {
        // Do something with the created folder
    }, function (err) {
        // Do something with the error message
    });

Web URL parameter can be null, if you want to use the client context for current web. Also, if you do not provide an error callback method, a simple alert with an error is displayed.

Image gallery from attachments in tooltips

Do you want to obtain a small image gallery display in a tooltip based on a files attached to an item?

Not too complicated to obtain this. We just need JavaScript tooltipster and DataTable library and CSS files. But first let’s build the code for getting attachments based on item id.

Type.registerNamespace('Shp');

Shp.Lists = function () {
            throw 'Cannot instantiate Shp.Lists static class';
};

 Shp.Lists.GetAttachments = function (listName, itemId, webUrl, success, fail) {
            /// <summary>Get attachments for an item</summary>
            /// <param name="listName" type="String" optional="false" mayBeNull="false">List name</param>
            /// <param name="itemId" type="Number" integer="true" optional="false" mayBeNull="false">List item</param>
            /// <param name="success" type="Function" optional="false" mayBeNull="false">Success</param>
            /// <param name="fail" type="Function" optional="true" mayBeNull="false">Fail</param>
            var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, mayBeNull: false, optional: false },
                                                            { name: 'itemId', type: Number, integer: true, mayBeNull: false, optional: false },
                                                            { name: 'webUrl', type: String, mayBeNull: true, optional: false },
                                                            { name: 'success', type: Function, mayBeNull: false, optional: false },
                                                            { name: 'fail', type: Function, mayBeNull: false, optional: true }], true);
            if (e) throw 'Shp.Lists.GetAttachments was called with invalid parameters. ' + e;
            var fail = fail || (function (err) { alert(err); });
            var ctx = (webUrl === null) ? SP.ClientContext.get_current() : new SP.ClientContext(webUrl);

            Shp.Lists._GetAttachments(listName, itemId, ctx, success, fail);
        };

     Shp.Lists._GetAttachments = function (listName, itemId, ctx, success, fail) {
            var oList = ctx.get_web().get_lists().getByTitle(listName);
            var oListItem = oList.getItemById(itemId);
            var files = oListItem.get_attachmentFiles();
            ctx.load(files);

            ctx.executeQueryAsync(function () {
                var results = [];
                for (var i = 0; i < files.get_count() ; i++) {
                    var file = files.itemAt(i);
                    results.push({ 'fileName': file.get_fileName(), 'serverRelativeUrl': file.get_serverRelativeUrl() });
                }
                success(results);
            }, function (sender, args) {
                fail(args.get_message());
            });
        };

Shp.Lists.registerClass('Shp.Lists');

Now, let have a look in XSLT code. We need it to display the data.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
  
    <xsl:output method="html" indent="yes"/>
    <xsl:template match="/">


      <div class="card-body">
          <table class="table table-striped table-bordered nowrap" id="dataTable" width="100%" cellspacing="0">
            <thead>
              <tr>
                <th>ID</th>
                <th>Product<br />Name</th>
                <th>Product<br />Code</th>
                <th>Description</th>
                <th>Country</th>
                <th>Supplier</th>
                <th>Available<br />Qty</th>
                <th>Currency</th>
                <th>Price</th>
              </tr>
            </thead>
            <tbody>
              <xsl:for-each select="/dsQueryResponse/Rows/Row">
                <tr>
                  <td>
                    <a title="Loading..." class="product-images" data-id="{@ID}">
                      <xsl:value-of select="@ID" disable-output-escaping="yes" />
                    </a>
                  </td>
                  <td>
                    <xsl:value-of select="@ProductName" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@ProductCode" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@ProductDescription" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@CountryCode" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@Supplier" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@Qty" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@Currency" disable-output-escaping="yes" />
                  </td>
                  <td>
                    <xsl:value-of select="@Price" disable-output-escaping="yes" />
                  </td>
                </tr>
              </xsl:for-each>
            </tbody>
          </table>

      </div>
         
    </xsl:template>
</xsl:stylesheet>

Our scope is to display a small image gallery in a toolip when we click in a link. So, for this, we need to run the following code when page loads.

jQuery('#dataTable').DataTable({
        scrollX: true,
        paging: false,
        fixedColumns: {
            leftColumns: 1
        },
        'aoColumnDefs': [
            { "bSortable": false, "aTargets": [0] }
        ],

        'drawCallback': function (settings) {
            jQuery('a[class*="product-images"]').tooltipster({
                trigger: 'click',
                contentAsHTML: true,
                interactive: true,
                theme: 'tooltipster-shadow',
                functionBefore: function (instance, helper) {
                    var $origin = jQuery(helper.origin);
                    var poId = $origin.attr('data-id');
                    if (Boolean($origin.data('loaded')) !== true) {
                        Shp.Lists.GetAttachments('Products list name', parseInt(poId), null, function (attachments) {
                            if (attachments.length === 0) {
                                instance.content('No image found');
                                $origin.data('loaded', true);
                                return;
                            }

                            var html = '';
                            for (var k = 0; k < attachments.length; k++) {
                                html += '<div class="mySlides">' +
                                    '<img src="' + attachments[k].serverRelativeUrl + '" style="width:100px; height: 100px">' +
                                    '</div>';
                            }
                            instance.content('<div class="slideshow-container">' + html + '</div>');

                        },
                            function (err) {
                                instance.content('Cannot get images:' + err);
                                $origin.data('loaded', true);
                            });
                    }
                }
            });
        }
    });

Of course we can apply different style on the gallery, but this is not the scope here. This is just to give you a start from where to start.

Attaching a file to a list item in SharePoint add-in model

Long time since I wrote about the SharePoint. Is still my job, I am still working on this, but I simply did not find the time to discover new things. Now, as SharePoint add-in model is more and more adopted in SharePoint development, due to the migration of large companies to Office 365, I am starting to face new challenges. One of this is how to attach a file to an existing list item.

<input type="file" id="attachment1" />

As HTML5 is the new standard for web, file control is able to read the content of a file as a binary string, so no more needed to server side languages. For rest, jQuery and SharePoint JavaScript libraries will do the work for you.


       Type.registerNamespace('Shp');

       Shp.Attachments = function () {
            /// <summary>Shp Attachments static class</summary>
            throw 'Cannot initiate Shp.Attachments static class';
        };

        Shp.Attachments.get_file = function (fileInput) {
            /// <summary>This method is used to get the content of the file as binary string</summmary>

            var deffered = jQuery.Deferred();
            var reader = new FileReader();
            reader.onload = function (e) { 
                deffered.resolve(e.target.result);
            };  

            reader.onerror = function (e) {
                deffered.reject(e.target.error);
            };

            reader.readAsBinaryString(fileInput.files[0]);
            return deffered.promise();
        };


        Shp.Attachments.add = function (listName, itemId, fileInput, webUrl, success, fail) {
            /// <summary>Add attachments</summary>
            /// <param>List name</param>
            /// <param>Item Id</param>
            /// <param>File input controls</param>
            /// <param>Web url</param>
            var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'itemId', type: String, optional: false, mayBeNull: false },
                                                            { name: 'filesInput', type: HTMLElement, optional: false, mayBeNull: false },
                                                            { name: 'webUrl', type: String, optional: false, mayBeNull: true },
                                                            { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                            { name: 'fail', type: Function, optional: true, mayBeNull: false }], true);
            if (e) throw e;


            var webUrl = webUrl || _spPageContextInfo.webAbsoluteUrl;
            var fail = fail || function (err) { alert(err); };

            Shp.Attachments.get_file(fileInput).then(function (fileContent) {
                var parts = fileInput.value.split("\\");
                var fileName = parts[parts.length - 1];
                // Attachments add internal method
                Shp.Attachments._add(listName, itemId, fileContent, fileName, webUrl, success, fail);
            });
        };

        Shp.Attachments._add = function (listName, itemId, fileContent, fileName, webUrl, success, fail) {

            var scriptBase = webUrl + "/_layouts/15/";
            jQuery.getScript(scriptBase + "SP.RequestExecutor.js", function () {

                var executor = new SP.RequestExecutor(webUrl);
                executor.executeAsync({
                    url: webUrl + "/_api/web/lists/GetByTitle('" + listName + "')/items(" + itemId + ")/AttachmentFiles/add(FileName='" + fileName + "')",
                    method: "POST",
                    binaryStringRequestBody: true,
                    body: fileContent,
                    state: "Update",
                    success: function () {
                        success(itemId);
                    },
                    fail: function (data) {
                        fail(data.responseText);
                    }

                });

            });
        };

        Shp.Attachments.registerClass('Shp.Attachments');

Once code is organized, attaching a file become an easy task.

 Shp.Attachments.add('Tickets', '2', document.getElementById('attachment1'), null, function (itemId) {
        alert(itemId);
    }, function (err) {
        alert('Error: ' + err);
    });

Shp.Attachments.add accepts following parameters:

  • List name as string
  • Item id as string
  • HTML file control as HTML element
  • Web URL. It can be nul and in this case is used _spPageContextInfo.webAbsoluteUrl
  • Success method to be executed if attachment is added
  • Fail method to be executed if attachment failed

Copy, adapt the code according to your needs and use it. Happy coding! If it helps, you can just say hi to me and I will be happy as well. 🙂

SharePoint survey and incomplete results

Is very easy to create SharePoint survey by the end users. Normally it covers all requirements, but there is only one minus. Very often, users click “Save and close” button instead of the “Next”, which creates incomplete results. Out of the box, you are allowed to see only complete results and only incomplete results created by you. That’s why I think is a very good idea to hide “Save and close” button and force users to complete all the questions. Definitely we can have some help from JavaScript.

function pageLoad() {
    var buttons = document.querySelectorAll('input[type="button"]');
    for(var j = 0; j < buttons.length; j++) {
        var button = buttons[j];
        if(button.value === 'Save and Close') {
            button.style.display = 'none';
        }
    }
}

Place this code in a text file, upload it on the server and display the content using a content editor web part you place on the page. Because you include everything in “pageLoad” function it will be executed when page is loading or after a postback.