Send email with classic asp

Below is an example function used in my classic asp code to send e-mail with Attachments. The e-mail is written in HTML format and attachments picked up from the physical path on the Server where the Application is hosted on IIS.

I’m using a hard-coded path in the sample below for the Attachment example. You can create a dynamic string by fetching the path from the DB.

I’m using CDOSYS mail provider object below to send e-mail.

Function GenerateEmailCEF(senderemail, recipient)
MailProvider = "CDOSYS"
EmailFmt = 0 'For HTML, 1 is for Plain Text ' SMTP Server Config
Dim rsSMTPSendUsing, rsSMTPServer, rsSMTPServerPort, rsSMTPSendUsername, rsSMTPSendPassword, rsSMTPUseSSL Dim SMTPSendUsing, SMTPServer, SMTPServerPort, SMTPSendUsername, SMTPSendPassword, SMTPUseSSL

SMTPSendUsing = "1"
SMTPServer = "smtp.xx.xx" 'Change as per your configuration.
SMTPServerPort = "25"
SMTPSendUsername = ""
SMTPSendPassword = ""
SMTPUseSSL = "false"

if MailProvider = "CDONTS" then set objMail=CreateObject("CDONTS.NewMail")

if MailProvider = "CDOSYS" then set objMail =CreateObject("CDO.Message")

objMail.From = senderemail
objMail.To = recipient
objMail.Subject = "Test Subject"

if MailProvider = "CDONTS" then objMail.MailFormat = EmailFmt
if MailProvider = "CDONTS" then objMail.BodyFormat = EmailFmt

'Created only HTML format
emailbod = "<html?<HEAD?<TITLE?</TITLE?</HEAD?<BODY?" 'Fix this
emailbod = emailbod & "<BR?Mail Body." 'Fix this

'Add attachments to mail.
newfn = "c:\Data\myfile.pdf" 'Path on the Server.

if MailProvider = "CDONTS" then execute ("objMail.AttachFile(" & newfn & ")")

if MailProvider = "CDOSYS" then execute ("objMail.AddAttachment(" & newfn & ")")

if MailProvider = "CDONTS" then objMail.Body = emailbod
if MailProvider = "CDOSYS" then objMail.HTMLBody = emailbod

objMail.BodyPart.Charset = "UTF-8"
objMail.HTMLBodyPart.Charset = "UTF-8"

if MailProvider = "CDOSYS" then
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = SMTPSendUsing
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SMTPServer
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = SMTPServerPort
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = SMTPUseSSL

if SMTPSendusername <> "" then
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusername") = SMTPSendusername
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendpassword") = SMTPSendPassword
objmail.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
end if

objmail.Configuration.Fields.Update
end if

objMail.Send
set objMail=Nothing
End Function

LogParser query example

I’ve found LogParser tool to be very useful for querying log files especially whenever I am required to analyze the IIS log files. You can download LogParser from here.

In this example, I’ll be querying multiple Log files unique users with Windows Authentication visiting the site. Click on the icon “Choose Log files/folders to query” and Add all files which you want to search. Open a New Query window and in the Query editor, enter the below query:

SELECT DISTINCT cs-username FROM '[LOGFILEPATH]'

This works much like SQL queries where IIS log headers work like columns. The above query will simply return distinct users visiting the site. Make sure the Log Type selected is W3CLOG.

If you want to Output all the data to a .csv file, then you can use the below query:

SELECT SELECT DISTINCT cs-username INTO '[OUTFILEPATH]users.CSV' FROM '[LOGFILEPATH]'

You can check the default export directory where the file is created. It should be something like this “C:\Users\<username>\AppData\Roaming\ExLPT\Log Parser Studio\Output”.

Download word file without Protected View .Net ReactJS

I have an Intranet Application where a .docm template downloaded from a ReactJS UI as a blob is opening in Protected View on the users’ machines. This is happening when downloading in browsers other than IE or Edge browser in IE mode.

When you download the document from Chrome or other browsers, MS Word is considering it as an Internet Application. Hence, the checkbox for Protected View is applying these changes and downloading it in Protected View.

Enable Protected View For Files Originating From The Internet. This setting controls documents opened from a website.

The code to download this as a blob is in ReactJS is available here.

However, if you generate the .docm file on the Server where the API is located and put it in a location on the Server itself. Create a Virtual Directory in IIS to make the document downloadable as http. So just return the hyperlink at the Client-side Javascript. You can receive this hyperlink and assign to anchor link clicking it through code.

var downloadUrl = response.data.Path;
        const link = document.createElement("a");
        link.id = "DownloadLink";
        link.href = downloadUrl;
        link.setAttribute("download", "filename");
        document.body.appendChild(link);
        link.click();
        document.getElementById("DownloadLink").outerHTML = "";

This will prevent it from opening in Protected View.

Also, if your word file has Macros, read-only fields become editable when you click on “Enable Editing” when you open the document in Protected View until you enable the Macros.

Change highlight color Autocomplete Material UI ReactJS

Create the below styles in your ReactJS component:

const styles = (theme) => ({
  ////....
  option: {
    // Hover
 with light-grey
    '&[data-focus="true"]': {
      backgroundColor: '#F8F8F8',
      borderColor: 'transparent',
    },
    // Selected
 has dark-grey
    '&[aria-selected="true"]': {
      backgroundColor: theme.palette.grey.A200,
      borderColor: 'transparent',
    },
  },
});

You can choose the colors based on your preference on selected and for mouse-over for backgroundColor property.

Add the following property in your AutoComplete component

classes={{
	  option: classes.option
	}}

as shown below:

<Autocomplete
	id="combo-box-1"
	options={myvalues}
	getOptionLabel={(option) => option.value}
	size="small"
	className={classes.formControlAutopopulate}
	classes={{
	  option: classes.option
	}}
	value={myvalues.find((x) => x.id === this.state.ID)}
	disabled={this.props.isLocked}
	onChange={this.handleSelected}
	renderInput={(params) => (
	  <TextField
		{...params}
		variant="outlined"
		placeholder="Please Select Value"
		fullWidth
	  />
	)}
/>

Borders not shown in Firefox with border-collapse on material table

I came across this issue that all Material Table used in the ReactJS Application are not showing bottom border in the th row in the Firefox browser. This issue is not reproducible in Chrome.

So, I found css hack where you can simply change the position attribute specifically for the Firefox browser in the default MuiTableCell-head class. This class is applied at the cell level by Material-table.

@-moz-document url-prefix() {
  .MuiTableCell-head {
    position: static !important;
  }
}

The above code will remove the default sticky value for position which causes the issue with border-collapse in the .MuiTable-root class. You can also remove the border-collapse in a similar fashion but do test that it’s not breaking anything else.

The component which has the Material-table code should import the above css to take effect. This hack might also work generally for html table if you’re facing a similar issue without Material-table.

Find duplicates in Excel

One of the most common scenarios of data cleansing is finding duplicates in Excel. Below are the steps:

Select the data range in Excel in which you want to find the duplicate values.

Under Home Tab, select Conditional Formatting->Duplicate Values:

Click OK and observe the highlighted cells with duplicate values.

You can now filter the data based on color to quickly check the duplicate values. Short-cut to filter is select the header of the column or columns and then Alt+D+F+F on keyboard.

Update Child Components from Parent ReactJS

One common challenge in ReactJS is updating Child Components from Parent Component in a form. I faced this scenario recently where after a series of GET calls where the order is decided by the browser threads, data is not always available in the same order.

If you need to set some permissions based on an API GET call and then enable/disable a child component, you can use the following ReactJS lifecycle method:

//Parent Component
shouldComponentUpdate(nextProps, nextState) {
    if (nextState.formReady) {
      return true;
    } else {
      return false;
    }
  }

Set the flag formReady in the series of successive GET calls to true, where you’re sure will be the last call on your Parent form.

//Parent Component GET call inside axios success.
this.setState(
{
  formReady: true,
},() => console.log(this.state.formReady);
);

The shouldComponentUpdate lifecycle method will check for the boolean value and will re-render or refresh the entire form including Child Components.

However, do refer to the documentation before using this method to decide if this suits your needs.

Upload word document to .Net core WebAPI

We’ll be working with a .docm template in Word 2016 which contains a textbox. This control is given a name using the Content Control Properties.

This is available under Developer Menu in MS Word, then click on Properties.

Save this template as .docm

Create .Net Core WebAPI under .net core version 3.1 for this example. The sample code is as below. The library System.IO is used to access the value in the variable.

using System;
using System.IO;
using System.IO.Packaging;
using System.Xml;
using System.Xml.XPath;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace SampleApplicationUpload.Controllers
{
    public class UploadController : ControllerBase
    {
        [HttpPost]
        [Route("upload")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public IActionResult Upload()
        {
            string filename = string.Empty;
            string extension = string.Empty;
            string result = string.Empty;
            bool uploaded = false;
            string ModifiedBy = string.Empty;
            var request = HttpContext.Request;

            if (request.Form.Files.Count > 0)
            {
                filename = request.Form.Files[0].FileName;
                extension = filename.Substring(filename.LastIndexOf(".")).ToLower();
                if (!string.IsNullOrEmpty(extension) && extension.Equals(".docm"))
                {
                    using (var memoryStream = new MemoryStream())
                    {
                        request.Form.Files[0].CopyTo(memoryStream);
                        if (memoryStream.Length < 2097152)
                        {
                            result = UploadWorStream(memoryStream, extension, out ModifiedBy, out uploaded);
                        }
                        else
                        {
                            return BadRequest("File size too large");
                        }
                    }
                }
                else
                {
                    return BadRequest("File format not recognised. Upload .docm file only");
                }
            }           
            return Ok(result);
        }
        private string UploadWorStream(MemoryStream ms, string ext, out string ModifiedBy, out bool uploaded)
        {
            string contenttype = string.Empty;
            bool erroroccurs = false;
            ModifiedBy = string.Empty;
            var xmlDoc = new XmlDocument();
            uploaded = false;
            switch (ext)
            {
                case ".docm":
                    contenttype = "application/vnd.ms-word";
                    break;
                default:
                    erroroccurs = true;
                    break;
            }

            if (!erroroccurs)
            {
                if (contenttype != string.Empty)
                {
                    Stream fs = ms;
                    var br = new BinaryReader(fs);
                    string xmlDocRelType = "http://schemas.openxmlformats.org/officeDocument/" +
                                           "2006/relationships/customXml";
                    string officeDocRelType = "http://schemas.openxmlformats.org/" +
                                              "officeDocument/2006/relationships/officeDocument";
                    PackagePart xmlPart = default(PackagePart);
                    PackagePart documentPart = default(PackagePart);
                    Uri documentUri = default(Uri);
                    Uri xmlUri = default(Uri);
                    using (Package officePackage = Package.Open(fs, FileMode.Open, FileAccess.Read))
                    {
                        foreach (var relationship1 in officePackage.GetRelationshipsByType(officeDocRelType))
                        {
                            documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative),
                                                                       relationship1.TargetUri);
                            documentPart = officePackage.GetPart(documentUri);
                            break;
                        }

                        if (documentPart != null)
                            foreach (var relationship2 in documentPart.GetRelationshipsByType(xmlDocRelType))
                            {
                                xmlUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative),
                                                                      relationship2.TargetUri);
                            }
                        xmlPart = officePackage.GetPart(xmlUri);
                        Stream inputStream = xmlPart.GetStream(FileMode.Open, FileAccess.Read);
                        var ts = new StreamReader(inputStream);
                        xmlDoc.Load(ts);
                        ts.Close();
                        ts.Dispose();
                        officePackage.Close();
                    }

                    XPathNavigator xpnGet2 = xmlDoc.CreateNavigator();
                   // string txtSample = xpnGet2.Evaluate("string(//ROOT[1]/SampleDoc[1]/@txtSample)").ToString();
                    string txtSample = Convert.ToString(xpnGet2.Evaluate("string(//ROOT/SampleDoc/@txtSample)"));
                    if (txtSample.Length > 0)
                    {
                        return txtSample;
                    }
                    else {
                        return "Unable to read document";
                    }
                }
                else
                {
                    uploaded = false;
                    return "Error";
                }
            }
            else
            {
                uploaded = false;
                return "Error";
            }
        }
    }
}

Finally try to access the WebAPI using a Postman request and check the value while debugging the end-point.

curl --location --request POST 'https://localhost:<port>/upload' \
--form 'attachment
=@/C:/Users/username/Desktop/TestDocument.docm'

Preparing React Native App for Android Release on Mac

When your React Native Application is ready for release to the Android Platform, you need to perform the following steps to create the .aab file or Android App Bundle which contains your compiled code and resources and you can upload to the Play Store. The APK generation and signing is done by Google Play.

I’m using React Native 0.62.2 for this post in VS Code.

Open Terminal and check the Java Home Path:

/usr/libexec/java_home

cd to the path displayed as shown in the image.

Run the keytool command as shown using sudo to generate the keystore and alias as shown below:

sudo keytool -genkey -v -keystore some-helper-key.keystore -alias some-helper-key-alias -keyalg RSA -keysize 2048 -validity 10000

As shown in the screen-shot, answer the questions that pop and your keystore will be generated under the Java Home folder.

Copy that keystore file and paste it in your Android folder under app in your React Native Project.

Modify the android/gradle.properties file and add:

MYAPP_UPLOAD_STORE_FILE=some-helper-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=some-helper-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=***
MYAPP_UPLOAD_KEY_PASSWORD=***

You might not want to store the passwords directly in the above configuration and store them in the keychain Access App, then the last 2 password configs are not required.

Add the Release signingconfig section to the android/app/build.gradle file:

signingConfigs {
        debug {
            .....
        }
        release {
            if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            }
        }
    }

cd to the Android folder and run:

./gradlew bundleRelease

The .aab file is generated as: android/app/build/outputs/bundle/release/app-release.aab

Now this is ready to be uploaded to the Play Store.

Issues Faced:

Resource and asset merger: Duplicate resources

The above error occurred for files under drawable folder, app.json, node_modules_reactnativevectoricons_glyphmaps_materialcommunityicons.json etc.

Go to android/app/src/main/res and delete all the directories starting with drawables and the above mentioned .json files. Do not delete the raw folder if it contains any resources.

If you want to directly test the Release version on your device, run the following command under Root folder of the Project:

react-native run-android --variant=release

You can install bundle tool on Mac:

brew install bundletool

To extract the APK directly from the .aab file, run the following commands:

bundletool build-apks --bundle=/MyApp/my_app.aab --output=/MyApp/my_app.apks

If you want to test it on a device, then also include the App signing information:

bundletool build-apks --bundle=/MyApp/apprelease.aab --output=/MyApp/my_app.apks
--ks=/MyApp/keystore.jks
--ks-pass=pass:***
--ks-key-alias=MyKeyAlias
--key-pass=pass:***

Please note that the .aab file should not contain hyphen else it’ll give file not found error.

If you want to generate a single Universal apks file, run the below command. Copy the .aab and .keystore files to the folder from where you’ll run bundle tool using terminal:

bundletool build-apks --bundle=MyApp.aab --output=MyApp.apks --mode=universal --ks=my-key.keystore --ks-pass=pass:**** --ks-key-alias=my-key-alias --key-pass=pass:****

Change the extension .apks to .zip and extract the universal.apk file. You can rename this file and transfer it to your Android device for installation.

You can also install the .apks directly to your connected device using bundle tool:

bundletool install-apks --apks=MyApp.apks

Create New Team Project TFS 2015

Most of the Admin features are available for TFS 2015 through the Browser Console. These can be managed using the URL:

http://servername:8080/tfs/_admin

If you need to create a new Team Project under an existing Collection, you need to be part of the Project Collection Administrators Group for that collection in TFS 2015.

The URL to access that is :

http://servername:8080/tfs/collectionname/_admin/_security/?_a=members

You can add Windows user or group or TFS group.

To create a New Team Project, go to the following URL:

http://servername:8080/tfs/collectionname/_admin

and click on New Team Project and fill out the required details and click on Create Project.

This will reflect in the Source Control Explorer in Visual Studio where you can then start creating branches.