INSTALLING SUBVERSION ON LAN

Testing Subversion client TortoiseSVN (windows shell) and Subversion plugin for Visual Studio AnkhSVN.

On client side:

  1. Install TortoiseSVN 1.4.8 built against Subversion 1.4.6 Released 16th February 2008

Go to: http://tortoisesvn.net/downloads

  and install the following

32 bit – TortoiseSVN-1.4.8.12137-win32-svn-1.4.6.msi

Once you are done, TortoiseSVN will be integrated into your Windows Explorer as shown below. Right click on any folder and you’ll see Tortoise options.

Note: If you see two TortoiseSVN options in the same menu, this is NOT a bug!

2– Install AnkhSVN, a Subversion plugin for Visual Studio

Go to http://ankhsvn.open.collab.net/ and download the installer

   ! NOTE: CLOSE VISUAL STUDIO FIRST ( it will prompt you anyways )

3-

  1. To check out solution files:
    1. create a folder where you want your solution files and right click on SVN Checkout
    1. select the repository location, this will create a working copy of the project files in your local working directory
    1. to begin coding, open your solution, or any other file, and start coding. You can open it through visual studio as well
    1. to commit your changes and push them back to repository, and for the time being, do not use ANKHSVN, rather, right click on the files you want to commit, and select commit from TortoiseSVN menu
    1. REBOOT YOUR PC ONCE DONE

Helping students along the way program in C#

Below was the requirement for one of the college students I helped for the final exam in programming. C# language was the choice.

Using C#,console application, do the following:
You are to create a menu system using switch / case statement.Each question should be in its own function. As well you can call functions from within functions.

Main Menu
1.Students and Grades
2.Rainfall Statistics
3.Charge Account Validation
4.Payroll
5.Drivers licence exam
6.Quit this menu
Enter your choice

If the user enters an invalid choice, display an error message. Stay in the menuuntil the user enters 6.

1.Students and Grades

a.Ask the user for the number of students. No negatives or 0 allowed.
b.Ask the user for the number of tests per student. No negatives or 0 allowed.
c.For each of the students, prompt the user to enter a grade for each of the tests.Input grade must be >=0 (MIN) and <=100(MAX)
d.Once all the tests are entered, display the sum of the grades, as well as the average for each student.

2.Rainfall Statistics

Design a function that lets the user enter the total rainfall for each month of a year into an array.The program should use functions to calculate and display the total rainfall for the year(do this entirely in the function), another function to compute and return the average monthly rainfall(display the average in your main),and create a function to display the months with the highest and lowest amounts of rain.

3.Charge Account Validation

Design a function that asks the user to enter a charge account number. The program should then call a function to derermine whether the number is valid by comparing it to the following list of valid charge account numbers:
5658845,4520125,7895122,8777541,8451277,1302850,8080152,4562555,5552012,5050552,7825877,1250255,1005231,6545231,3852085,7576651,7881200,4581002
The function simply returns a boolean value. The above valid charge numbers should be stored in an array.Use the sequential search algorithm to locate the number entered by the user. If the number is in the array, the program should display a message indicating the number is valid. If the number is not in the array, the program should display a message indicating the number is invalid.

4.Payroll

Design a function that uses the following parallel arrays:
-empId:An array of seven integers to hold employee identification numbers.The array should be initialized with the following numbers:56588,45201,78951,87775,84512,13028,75804
-hours:An array of seven integers to hold the number of hours worked by each employee.
-payRate: An array of seven doubles to hold each employee’s hourly pay rate.
-wages:An array of seven doubles to hold each employee’s gross wages.

The program should relate the data in each array through the subscripts. For example, the number in element 0 of the hours array should be the number of hours worked by the employee whose identification number is stored in element 0 of the empId array. That same employee’s pay rate should be stored in element 0 of the payRate array.The program should display each employee number and ask the user to enter that employee’s hours and pay rate. It should then calculate the gross wages for that employee(hours times pay rate), which should be stored in the wages array.After the data has been entered for all the employees, the program should display each employee’s identification number and gross wages.To do this, create another function – to which you pass an index – to display each employees identification number and gross wages. Question to think about(no you do not have to do anuthing for the assignment)

5.Driver’s License Exam

The local driver’s license office has asked you to write a program that grades the written portion of the driver’s license exam.The exam has 10 multiple choice questions. Here are the correct answers:
1.B, 2.D, 3.A, 4.A, 5.C, 6.A, 7.B, 8.A, 9.C, 10.D
You are to use functions and arrays for this question.You decide how many functions you would need.Your program should read the correct answers from an array.It should ask the user to enter the students’s answers for each of the ten questions, and the answers should be stored in another array.After the student’s answers have been entered, the program should display a message indicatingwhether the student passed or failed the exam.(A student must correctly answer at least 6 of the 10 questions to pass the exam)
After all the answers have been inputted, a report should display:
-A list showing the question number with the correct answer, entered answer and Correct / Incorrect result.
-The total number of correctly answered questions
-The total number of incorrectly answered questions.
Input validation: Only accept the letters A, B, C, or D as answers and an error message if anything else is entered.

I used Visual Studio 2017 for this. Open it and create new Console Application. The code below is in the Program.cs file,which contains static void Main(string[] args) start function and entry point for the program:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainProject
{
class Program
{
//Drivers License Variable
int totalNumberOfCorrectAnswers = 0;
static void Main(string[] args)
{
Console.WriteLine("Main Menu");
Console.WriteLine("1. Students & Grades");
Console.WriteLine("2. Rainfall Statistics");
Console.WriteLine("3. Charge Account Validation");
Console.WriteLine("4. Payroll");
Console.WriteLine("5. Drivers Licence Exam");
Console.WriteLine("6. Quit this menu");
Console.WriteLine("Enter your choice");
int uInput = Convert.ToInt32(Console.ReadLine());
do
{
switch (uInput)
{
case 1:
{
// Console.WriteLine("Students & Grades");
StudentsAndGrades();
break;
}
case 2:
{
//Console.WriteLine("Rainfall Statistics");
RainfallStatistics();
break;
}
case 3:
{
//Console.WriteLine("Charge Account Validation");
ChargeAccountValidation();
break;
}
case 4:
{
//Console.WriteLine("Payroll");
Payroll();
break;
}
case 5:
{
//Console.WriteLine("Drivers Licence Exam");
DriversLicenceExam();
break;
}
default:
{
//Console.WriteLine("ERROR: Please enter a number between 1 and 6");
Environment.Exit(0);
break;
}
}
uInput = Convert.ToInt32(Console.ReadLine());
}
while (uInput != 6);
}
private static void StudentsAndGrades()
{
int numberOfStudents;
int numberOfTestsPerStudent;
//Number of students
do
{
Console.WriteLine("How many students ?");
numberOfStudents = Convert.ToInt32(Console.ReadLine());
}
while (numberOfStudents < 0 || numberOfStudents == 0);
do
{
//Number of tests per student
Console.WriteLine("How many tests ?");
numberOfTestsPerStudent = Convert.ToInt32(Console.ReadLine());
}
while (numberOfTestsPerStudent < 0 || numberOfTestsPerStudent == 0);
//Grade per student pair
var TestsPerStudentlist = new List<KeyValuePair<int, int>>();
int s = 0;
//per each student
do
{
int t = 0;
//per each test
do
{
Console.WriteLine("Enter grade for student " + (s + 1) + " test " + (t + 1) + " : ");
int sGrade = Convert.ToInt32(Console.ReadLine());
TestsPerStudentlist.Add(new KeyValuePair<int, int>((s + 1), sGrade));
t++;
}
while (t < numberOfTestsPerStudent);
s++;
}
while (s < numberOfStudents);
// output results
// number of students* tests
double globalAverage = 0.0;
for (int student = 0; student < numberOfStudents; student++)
{
int result = TestsPerStudentlist.Where(x => x.Key == (student + 1)).Sum(x => x.Value);
string studentsAverage = Convert.ToString((double)(result / numberOfTestsPerStudent));
globalAverage = globalAverage + Convert.ToDouble(studentsAverage);
Console.WriteLine("For student " + (student + 1) + " the average is: " + studentsAverage + " and the sum of grades is " + result);
}
Console.WriteLine();
Console.WriteLine("The average of the average of all students is: " + (double)(globalAverage / numberOfStudents));
Console.WriteLine("Enter MAIN MENU choice");
}
private static void RainfallStatistics()
{
//Rainfall Statistics
int counter = 0;
string[] Months = new string[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
var RainfallPerMonth = new List<KeyValuePair<int, int>>();
do
{
Console.WriteLine("Enter the total rainfall for " + (Months[counter].ToString()) + ":");
int rainfallUnits = Convert.ToInt32(Console.ReadLine());
RainfallPerMonth.Add(new KeyValuePair<int, int>(counter, rainfallUnits));
counter++;
}
while (counter < Months.Length);
int totalRainfallForTheYear = TotalRainfallForTheYear(RainfallPerMonth);
double averagemonthlyRainfall = AverageMonthlyRainfall(RainfallPerMonth);
int[] highestAndLowest = HighestMonthAndLowest(RainfallPerMonth);
Console.WriteLine("The total rainfall for the year is: " + totalRainfallForTheYear);
Console.WriteLine("The average monthly rainfall is: " + averagemonthlyRainfall);
Console.WriteLine("Month with the highest amount of rain: " + Months[highestAndLowest[0] - 1].ToString());
Console.WriteLine("Month with the lowest amount of rain: " + Months[highestAndLowest[1] - 1].ToString());
Console.WriteLine("Enter MAIN MENU choice");
}
private static void ChargeAccountValidation()
{
//Charge Account Validation
int[] ValidChargeAccountNumbers = new int[18] { 5658845, 4520125, 7895122, 8777541, 8451277, 1302850, 8080152, 4562555, 5552012, 5050552, 7825877, 1250255, 1005231, 6545231, 3852085, 7576651, 7881200, 4581002 };
Console.WriteLine("Charge Account Validation - Sequential Search");
Console.WriteLine("Please enter a number");
int uInput = Convert.ToInt32(Console.ReadLine());
bool isValidNumber = IsValid(ValidChargeAccountNumbers, uInput);
if (isValidNumber)
{
Console.WriteLine("The number is VALID !");
}
else
{
Console.WriteLine("The number is NOT VALID !");
}
Console.WriteLine("Enter MAIN MENU choice");
}
private static void Payroll()
{
int[] empId = new int[7] { 56588, 45201, 78951, 87775, 84512, 13028, 75804 };
int[] hours = new int[7];
double[] payRate = new double[7];
double[] wages = new double[7];
int t = 0;
//per each test
do
{
Console.WriteLine("Employee number: " + empId[t]);
Console.WriteLine("Enter hours: ");
hours[t] = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter pay rate: ");
string doublePayRate = Console.ReadLine();
double tmp = Convert.ToDouble(doublePayRate, CultureInfo.InvariantCulture);
payRate[t] = tmp;
Console.WriteLine("----------------------------------------");
t++;
}
while (t < 7);
//Calculating gross wages for each employee
wages[0] = Convert.ToDouble(hours[0], CultureInfo.InvariantCulture) * payRate[0];
wages[1] = Convert.ToDouble(hours[1], CultureInfo.InvariantCulture) * payRate[1];
wages[2] = Convert.ToDouble(hours[2], CultureInfo.InvariantCulture) * payRate[2];
wages[3] = Convert.ToDouble(hours[3], CultureInfo.InvariantCulture) * payRate[3];
wages[4] = Convert.ToDouble(hours[4], CultureInfo.InvariantCulture) * payRate[4];
wages[5] = Convert.ToDouble(hours[5], CultureInfo.InvariantCulture) * payRate[5];
wages[6] = Convert.ToDouble(hours[6], CultureInfo.InvariantCulture) * payRate[6];
for (int j = 0; j < 7; j++)
{
displayWages(wages, empId, j);
}
Console.WriteLine("Enter MAIN MENU choice");
}
private static void DriversLicenceExam()
{
var CorrectExamAnswers = new List<KeyValuePair<int, string>>();
CorrectExamAnswers.Add(new KeyValuePair<int, string>(1, "B"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(2, "D"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(3, "A"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(4, "A"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(5, "C"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(6, "A"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(7, "B"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(8, "A"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(9, "C"));
CorrectExamAnswers.Add(new KeyValuePair<int, string>(10, "D"));
Console.WriteLine("Please enter user's answers: ");
var StudentExamAnswers = new List<KeyValuePair<int, string>>();
string ans;
int t = 0;
//per each test
do
{
//validation
do
{
Console.WriteLine("Answer for Question " + (t + 1) + " : ");
ans = Console.ReadLine();
}
while (ans != "A" && ans != "B" && ans != "C" && ans != "D");
StudentExamAnswers.Add(new KeyValuePair<int, string>((t + 1), ans));
t++;
}
while (t < 10);
bool PassOrFail = passFail(CorrectExamAnswers, StudentExamAnswers);
//NEW LINE
Console.WriteLine();
// Displaying passed or failed
if (PassOrFail)
Console.WriteLine("PASSED");
else
Console.WriteLine("FAILED");
//NEW LINE
Console.WriteLine();
// Display stats graph
Console.WriteLine("Question |Answer |User Input |Result");
Console.WriteLine("------------------------------------------------");
for (int i = 0; i < 10; i++)
{
//FORMATTING DIGIT 10
if (i == 9)
{
Console.WriteLine((i + 1) + "." + " |" + CorrectExamAnswers[i].Value.ToString() + " |" + StudentExamAnswers[i].Value.ToString() + " |" + (CorrectExamAnswers[i].Value.ToString().Trim() == StudentExamAnswers[i].Value.ToString().Trim() ? "Correct" : "Incorrect"));
}
else
{
Console.WriteLine((i + 1) + "." + " |" + CorrectExamAnswers[i].Value.ToString() + " |" + StudentExamAnswers[i].Value.ToString() + " |" + (CorrectExamAnswers[i].Value.ToString().Trim() == StudentExamAnswers[i].Value.ToString().Trim() ? "Correct" : "Incorrect"));
}
}
Console.WriteLine("Total number of correct answers: " + GetTotalNumberOfCorrectAnswers(CorrectExamAnswers, StudentExamAnswers));
Console.WriteLine("Total number of incorrect answers: " + (10 - GetTotalNumberOfCorrectAnswers(CorrectExamAnswers, StudentExamAnswers)));
Console.WriteLine("Enter MAIN MENU choice");
}
//helper functions
static int TotalRainfallForTheYear(List<KeyValuePair<int, int>> rainPerMonth)
{
int totalRainfallForTheYear = 0;
for (int i = 0; i < rainPerMonth.Count; i++)
{
totalRainfallForTheYear = totalRainfallForTheYear + rainPerMonth[i].Value;
}
return totalRainfallForTheYear;
}
static double AverageMonthlyRainfall(List<KeyValuePair<int, int>> rainPerMonth)
{
int totalRainfallForTheYear = 0;
double averageMonthlyrainfall = 0.0;
totalRainfallForTheYear = TotalRainfallForTheYear(rainPerMonth);
averageMonthlyrainfall = totalRainfallForTheYear / 12;
return averageMonthlyrainfall;
}
static int[] HighestMonthAndLowest(List<KeyValuePair<int, int>> rainPerMonth)
{
int[] highestLowest = new int[2];
string highestMonth = "";
string lowestMonth = "";
int maxmonth = Convert.ToInt32(rainPerMonth.Max(y => y.Value));
int minmonth = Convert.ToInt32(rainPerMonth.Min(y => y.Value));
highestLowest[0] = maxmonth;
highestLowest[1] = minmonth;
return highestLowest;
}
private static bool IsValid(int[] validchargeaccnumbers, int userInput)
{
bool isValid = false;
for (int i = 0; i < validchargeaccnumbers.Length; i++)
{
if (validchargeaccnumbers[i] == userInput)
{
isValid = true;
break;
}
}
return isValid;
}
private static void displayWages(double[] wges, int[] employeeids, int empIndex)
{
Console.WriteLine("Employee Identification Number: " + employeeids[empIndex] + "- Gross Wage: " + wges[empIndex]);
}
private static bool passFail(List<KeyValuePair<int, string>> correctAnswers, List<KeyValuePair<int, string>> stuAns)
{
bool pass = false;
int passFailcounter = 0; // 6 out of 10 should match
for (int i = 0; i < 10; i++)
{
if (correctAnswers[i].Value == stuAns[i].Value)
{
passFailcounter = passFailcounter + 1;
}
}
if (passFailcounter >= 6)
pass = true;
else
pass = false;
return pass;
}
private static int GetTotalNumberOfCorrectAnswers(List<KeyValuePair<int, string>> correctAnswers, List<KeyValuePair<int, string>> stuAns)
{
bool pass = false;
int passFailcounter = 0; // 6 out of 10 should match
for (int i = 0; i < 10; i++)
{
if (correctAnswers[i].Value == stuAns[i].Value)
{
passFailcounter = passFailcounter + 1;
}
}
return passFailcounter;
}
}
}
view raw Program.cs hosted with ❤ by GitHub

Accessing and manipulating your raw DataTable data with LINQ

As the heading suggests, I want to discuss a little bit on how to manipulate raw DataTable data that you get from some source. Mostly, and in many cases it is database, an SQL database maybe, where you fill your DataTable with rows from one or more SQL tables.

In the end , when you fill your DataSet object or, for the sake of this example, DataTable object, you can use that data in your business or front end Razor logic. You can use it wherever you want.

Let’s jump directly to code:

var DistinctGroupedColumns = dt.AsEnumerable().GroupBy(x => x.Field<Int32>("COLUMNONE")).Distinct().ToList();
foreach (var date in DistinctGroupedColumns)
{
//For each distinct record, get the associated list of records
var associatedListOfRecords = (from alofrec in dt.AsEnumerable() where alofrec.Field<Int32>("COLUMNONE").Date.ToString() == date.Key.ToString()
select alofrec).ToList();
int i = 0;
//The foreach below gets associated records per grouped group.
foreach (var timePart in associatedListOfRecords)
{
var COLUMNONE = Convert.ToDateTime(timePart.Field<DateTime>("CDateTime")).Date.ToString("dd/mm/yyyy");
var COLUMNTWO = Convert.ToDateTime(timePart.Field<DateTime>("CDateTime")).ToString("HH:mm");
var COLUMNTHREE = timePart.Field<String>("PhoneNumber").ToString();
var COLUMNFOUR = timePart.Field<String>("AccountNumber").ToString();
var COLUMNFIVE = timePart.Field<String>("HouseNumber").ToString();
// Do something with the column values, like print an HTML grid in Razor View, or send an SMTP email with those values.
// i is an additional variable, just in case, to keep record count for each group
i = i + 1;
}
}

On line 1, above, we get distinct list of rows based on COLUMNONE grouping. This will tell us how many groups and sections we have.As you can see, we already know the data type of our column. This is written and should be known manually by physically going to the database or reading back end logic associated with your datatable data.

On line five, we again run a query on our DataTable but filter it with LINQ Key keyword. The key is the property of the object on which we have done grouping before.

Then, on line 10, we iterate our list and get the column details values from it.

This code, as you can see in the comments, can be added on server side, and on client side in Razor View between the @{} .

The i, is there to hold the count of total items in each group.

Speeding up, protecting, and fixing your PC. How to.

Speeding up, protecting, and fixing your PC. How to.

If your PC, running any windows operating system slows down, internet is slow, and programs run slow, you might want to do all those items below, they will definitely help if not fix it completely.

Before doing all this, and if you do not already do this, back up all your needed data. If you never backed up, you can download EaseUS Todo Backup free version, back up all your data for the first time, and then set Incremental Backup on daily basis. It will take long time to backup whatever you selected for the first time, but then it only adds up the changes with Incremental Backup, as if it is version control. A very nice tool to have around !

You can do the following, in the following order:

  1. Buy a good antivirus, and it does not matter what Windows you are running. Now, since windows 10 comes with its own antivirus, it may not be enough, because it focuses on scanning only one part of the problem or viruses, while forgetting about the others.

The best antivirus packages currently on the market are the following, based on your budget, my personal choices, from best to least effective:

  1. Kaspersky Small Office Security:
    •  Price: 148 $ CAD for 1 year
    • 5 users
    • Fileserver
    • 5 mobiles
    • Single web page displays security status of all devices
    • Speed optimization
    • Secure web + email
    • Protects information on employee devices
  2. McAfee Small Business Security
    • Price: 80$ CAD per year
    • 5 computers
    • Unlimited mobile devices
    • Monitor all devices from single place
    • Locate, wipe, lock stolen device
  3. Bit Defender Small Office Security
    • Price: 150 $ CAD
    • Protects hackers from exploiting systems
    • Cloud based centralized control
  4. Bit Defender ( Internet Security )
    • Price:
      • 10 devices: 90$ CAD
      • 5 devices : 85 $ CAD
      • 3 devices : 30 $ CAD
      • 1 device : 60 $ CAD
    • Internet security
    • Ransomware protection
    • Parental control
    • Network threat protection
    • Includes bit defender VPN
    • Includes bit defender safe pay
    • Support 24/7

After you install one of the products above, and I recommend Kaspersky, scan everything, especially Trojan Horses and Malware that might slow down your internet. Those viruses are hard to find by yourself.

After the scan perform the following tuning tasks:

  • Go to control panel and search for power options.
  • Change power plan to High Performance
  • Increase virtual memory. The formula to increase your virtual memory is as follows: Initnial Size: (1.5) times total system memory ( RAM ), in MegaBytes. Maximum Size: (3) times Initial Size.
  • Scan RAM for problems, and to do this:
    • Go to Control Panel –> Administrative Options –> Windows Memory Diagnostic –> Click on “Restart Now and check for problems”
    • It will scan, and restart multiple times but it will check for defected RAM memory sectors
  • Install Hard Disk Validator and scan all hard disk memory for corrupted and bad sectors. It repairs corrupted ones as well.
  • Remove all unnecessary Startup Programs and services that start when you boot up your computer.To do this:
    • On windows 7 press Windows + R and type msconfig, go to startup and uncheck all the not needed programs. That speeds up startup.
    • On windows 10, press CTRL + SHIFT + ESC, and also go to Startup and remove unneeded programs that take startup memory.
  • Consider updating your BIOS, a program your personal computer microprocessor uses to get the computer system started after you turn it on.But backup your data first because sometimes, because of viruses, bios hangs up, and breaks, causing unbootable computer. Backup and create a bootable disk first, then update BIOS.You can find your new BIOS from manufacturer’s website.

After you have done all this, there are two things still to do:

  1. If programs open fast, run fast, save fast. Opening multiple programs is fast too. But, internet is slow. Check your providers’s speed test couple of times per day, for several days. Change your Internet Provider to a different one, better one. Note: When doing test speed, plug in the internet cable that comes from your Provider directly into your computer, with no router.
  2. If programs run slow, consider buying new PC.A good PC would be with the following specifications:

-DELL OPTIPLEX
-Processor: I7
-8 GB RAM
-500 GB Hard Disk
-Windows 10 Pro

OR

-DELL or HP
-Processor: I5
-RAM: 4GB
-Hard Disk: 260 GB
-Windows 10

Generally speaking, the more RAM the better.

Displaying Spinner in ASP.NET MVC App while file is downloading plus getting time it took.

spinner

Seems easy and looks pretty much simple. One might say : “Hey, just start download, display the image,and then hide it ! That’s it !”.

In a way, that is true. That is the general idea. But there are a couple of hidden surprises. For example, you always trigger the download on the server side. Often, and in many cases, we use Response.End, to close the binary response stream that is letting us download the file. If you want to continue doing something after the download, you will not be able to, because Response.End closes everything, even AJAX success return event, including access to the session state variables in ASP.NET MVC. So avoid it. Just binary write the file, and continue doing whatever you are doing.
Also, cross browser spinner is little bit tricky, because Firefox is very keen on image paths,and Internet Explorer has its own world, and css properties like display: none; get messy when trying to hide the image with JQuery.show() and JQuery.hide() functions.

So let’s start from the user interface. This logic can be applied to any front end pages, nor necessarily ASP.NET MVC and Razor view. HTML, ASP, JSP, and so on.

We will first download free GIF, create Images directory in our ASP.NET MVC app, and add it right there, then right click on that directory and click on Add Existing Item. Select the image and hit ok. This is so that Visual Studio knows about this image in its Solution and Project resources. Safer.

In our _Layout.cshtml, we are going to modify its style, and just add jquery reference, from the folder that Visual Studio has already created for us when creating ASP.NET MVC App in Visual Studio 2017.Below is _Layout.chtml page


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title – My ASP.NET MVC Spinner and Download Time Stats</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
</head>
<body>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p><a href="https://thoughtsonprogramming.wordpress.com/">thoughtsonprogramming.wordpress.com</a></p&gt;
</footer>
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
</html>

Then we switch to our Index.cshtml. And set HTML if-else for Internet Explorer to avoid display:none; and JQuery errors, and add the following code to show our spinner, which we want it to be at the center of the screen:


<div class="row">
<div class="col-md-4">
&nbsp;
</div>
<div class="col-md-4">
<!– [if !IE] –>
<p style="text-align:center;display: none;" id="animatedSpinnerContainer" class="animatedSpinnerContainer"><img src="~/Images/spinner.gif" /></p>
<!– [endif] –>
<!–[if gte IE 9 –>
<p style="text-align:center" id="animatedSpinnerContainer" class="animatedSpinnerContainer"></p>
<!–<![endif]–>
</div>
<div class="col-md-4">
&nbsp;
</div>
</div>

Then we need to add a form with a download button, that once clicked on it will trigger the download ActionResult:


<div class="container bg-success">
<h1>Showing spinner while waiting for download to finish</h1>
<p class="lead">The spinner below will appear for the duration of the download.Please click on the button below to start download.</p>
<form action="/Home/Download" method="post" id="downloadForm">
<p><input type="submit" class="btn btn-primary btn-lg" value="Download" /></p>
<input type="hidden" id="spinnerToken" name="spinnerToken" />
</form>
</div>

Now, once we click on the download button, we need the following JQuery function to handle the showing and hiding of spinner. This is where all the action happens:


$("#downloadForm").on("submit", function (event) {
/////////////show spiner////////////////
myGlobalToken = new Date().getTime(); //JavaScript time token
$('#spinnerToken').val(myGlobalToken); // set the hidden input our token value
var ua = window.navigator.userAgent;
var is_ie = /MSIE|Trident/.test(ua);
if (is_ie) {
//IE
$(".animatedSpinnerContainer").html("<img src=\"../../Images/spinner.gif\" />");
}
else {
$(".animatedSpinnerContainer").show();
}
//Check if the Session spinner token has been set, to compare it later
// on and hide the spinner if the condition is met
checkSessionState();
});

In the function above, once the form starts to submit, JQuery onSubmit triggers. We first generate our unique front end token:

myGlobalToken = new Date().getTime(); //JavaScript time token

then set the hidden form’s input to this token:

$(‘#spinnerToken’).val(myGlobalToken); // set the hidden input our token value

The next ten lines of code, check the browser or user agent, and if it is IE, set the GIF manually,because JQuery.show() and JQuery.hide() render unexpected results in IE. Here we show the spinner. The line checkSessionState(); calls a function that will check when we need to hide the spinner.

Now, at this stage we are in Code Behind and in our MVC ActionResult
Download, which looks like this:


[HttpPost]
public ActionResult Download(FormCollection fc)
{
//start the tier to measure the download time
System.Diagnostics.Stopwatch downloadTimer = new System.Diagnostics.Stopwatch();
downloadTimer.Start();
StringBuilder HTMLstring = new StringBuilder("<html><head></head><body><h1>Example PDF page. Spinner in ASP.NET MVC. thoughtsonprogramming.wordpress.com</h1></body></html>");
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=" + "PDFfile.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
var configurationOptions = new PdfGenerateConfig();
configurationOptions.PageOrientation = PdfSharp.PageOrientation.Landscape;
configurationOptions.PageSize = PdfSharp.PageSize.Letter;
configurationOptions.MarginBottom = 19;
configurationOptions.MarginLeft = 2;
var output = PdfGenerator.GeneratePdf(HTMLstring.ToString(), configurationOptions);
XFont font = new XFont("Segoe UI,Open Sans, sans-serif, serif", 7);
XBrush brush = XBrushes.Black;
var outputstream = new MemoryStream();
output.Save(outputstream);
var temp = fc["spinnerToken"].ToString().Trim();
//WARNING : This is only for this example REMOVE IN REAL WORLD SCENARIO !!!!!!!!!!!!!!!!!!!!!!!
//System.Threading.Thread.Sleep(5000);
//WARNING : This is only for this example REMOVE IN REAL WORLD SCENARIO !!!!!!!!!!!!!!!!!!!!!!!
downloadTimer.Stop();
string timeTookToDownload = downloadTimer.Elapsed.Days + " Days " + downloadTimer.Elapsed.Hours + " Hours " + downloadTimer.Elapsed.Minutes + " Minutes " + downloadTimer.Elapsed.Seconds + " Seconds " + downloadTimer.Elapsed.Milliseconds + " Milliseconds ";
Session["spinnerToken"] = fc["spinnerToken"].ToString().Trim();
Session["timetooktodownload"] = timeTookToDownload;
Response.BinaryWrite(outputstream.ToArray());
return View("Index");
}

In the code above, we start timer, to measure how long it took to download, build our HTML string that we will convert it to PDF file later on with the help of PDFSharp and HTMLRenderer, prepare page layout to be Letter, with appropriate sizes, depending on our needs, generate the actual PDF with:

var output = PdfGenerator.GeneratePdf(HTMLstring.ToString(), configurationOptions);

also, we can set Font and Brush to access each generated page individually and draw and change it at runtime, with XFont and XBrush objects, for example. Hint. With those two objects we can set header and footer on each PDF page, but this is not the goal of this article.

Then we stop the timer:

downloadTimer.Stop();

Get the time it took to render our PDF, and set our Session that value:

Session[“timetooktodownload”] = timeTookToDownload;

Also, Set the token we created earlier on and assigned our hidden form field to the session:

Session[“spinnerToken”] = fc[“spinnerToken”].ToString().Trim();

Meanwhile, while we are inside our Download ActionResult, another ActionResult triggers :

public ActionResult CheckForSpinnerSession, with the code below:


[HttpPost]
public ActionResult CheckForSpinnerSession()
{
string returnvalue = "false";
string fileDownloadTime = "false";
if (HttpContext.Session["spinnerToken"] != null)
{
returnvalue = Session["spinnerToken"].ToString();
}
if (HttpContext.Session["timetooktodownload"] != null)
{
fileDownloadTime = HttpContext.Session["timetooktodownload"].ToString();
}
return Json(new Models.DownloadStats { FileDownloadOK= returnvalue , FileDownloadTime= fileDownloadTime});
}

From the code above, we return two variables to check them later in JavaScript: FileDownloadOK, and FileDownloadTime. The JS code is below:


function checkSessionState() {
SessionChecker = window.setInterval(function () {
//alert("check session state started");
//check for session using AJAX again:
$.ajax({
type: "POST",
url: "/Home/CheckForSpinnerSession",
success: function (returnedData) {
//alert(r);
if (returnedData.FileDownloadOK == myGlobalToken) {
//alert("before hidespinner");
hideSpinner();
//$(".animatedSpinnerContainer").html("<h2>The time took to download the file is: </h2><br><b><font style=\"color:red\">" + returnedData.FileDownloadTime + "</font></b>");
alert(returnedData.FileDownloadTime);
}
}
});
}, 1000);
}

It triggers each second, checking for the Session FileDownloadOK, to know when to hide the spinner. If tokens match, we trigger another function : hideSpinner, as seen below:


function hideSpinner() {
window.clearInterval(SessionChecker);
var UserAgent = window.navigator.userAgent;
var InternetExplorer = /MSIE|Trident/.test(UserAgent);
if (InternetExplorer) {
//IE
$(".animatedSpinnerContainer").html("");
}
else {
$(".animatedSpinnerContainer").hide();
}
}

view raw

HideSpinner.js

hosted with ❤ by GitHub

As you can see, here we also check for Internet Explorer, and if it is we set HTML to empty string, instead of using hide() and show() from JQuery. For the rest of the browsers we use .hide() fucntion .

After we hide the spinner, we show the download statistics, and time, in the following line of code:


$(".animatedSpinnerContainer").
html("<h2>The time took to download the file is: </h2><br><b><font style=\"color:red\">" +
returnedData.FileDownloadTime +
"</font></b>");

The Front end page for our solution with all javascript code is below:


@{
ViewBag.Title = "Home Page";
}
<script type="text/javascript">
var myGlobalToken;
var SessionChecker;
$(document).ready(function () {
$("#downloadForm").on("submit", function (event) {
/////////////show spiner////////////////
myGlobalToken = new Date().getTime(); //JavaScript time token
$('#spinnerToken').val(myGlobalToken); // set the hidden input our token value
var ua = window.navigator.userAgent;
var is_ie = /MSIE|Trident/.test(ua);
if (is_ie) {
//IE
$(".animatedSpinnerContainer").html("<img src=\"../../Images/spinner.gif\" />");
}
else {
$(".animatedSpinnerContainer").show();
}
//Check if the Session spinner token has been set, to compare it later
// on and hide the spinner if the condition is met
checkSessionState();
});
function checkSessionState() {
SessionChecker = window.setInterval(function () {
//alert("check session state started");
//check for session using AJAX again:
$.ajax({
type: "POST",
url: "/Home/CheckForSpinnerSession",
success: function (returnedData) {
//alert(r);
if (returnedData.FileDownloadOK == myGlobalToken) {
//alert("before hidespinner");
hideSpinner();
//$(".animatedSpinnerContainer").html("<h2>The time took to download the file is: </h2><br><b><font style=\"color:red\">" + returnedData.FileDownloadTime + "</font></b>");
alert(returnedData.FileDownloadTime);
}
}
});
}, 1000);
}
function hideSpinner() {
window.clearInterval(SessionChecker);
var UserAgent = window.navigator.userAgent;
var InternetExplorer = /MSIE|Trident/.test(UserAgent);
if (InternetExplorer) {
//IE
$(".animatedSpinnerContainer").html("");
}
else {
$(".animatedSpinnerContainer").hide();
}
}
}); //document ready end
</script>
<div class="container bg-success">
<h1>Showing spinner while waiting for download to finish</h1>
<p class="lead">The spinner below will appear for the duration of the download.Please click on the button below to start download.</p>
<form action="/Home/Download" method="post" id="downloadForm">
<p><input type="submit" class="btn btn-primary btn-lg" value="Download" /></p>
<input type="hidden" id="spinnerToken" name="spinnerToken" />
</form>
</div>
<div class="row">
<div class="col-md-4">
&nbsp;
</div>
<div class="col-md-4">
<!– [if !IE] –>
<p style="text-align:center;display: none;" id="animatedSpinnerContainer" class="animatedSpinnerContainer"><img src="~/Images/spinner.gif" /></p>
<!– [endif] –>
<!–[if gte IE 9 –>
<p style="text-align:center" id="animatedSpinnerContainer" class="animatedSpinnerContainer"></p>
<!–<![endif]–>
</div>
<div class="col-md-4">
&nbsp;
</div>
</div>

And the Home Controller with all Action Results is below:


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using PdfSharp.Drawing;
using TheArtOfDev.HtmlRenderer.PdfSharp;
namespace Spinner.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
[HttpPost]
public ActionResult Download(FormCollection fc)
{
//start the tier to measure the download time
System.Diagnostics.Stopwatch downloadTimer = new System.Diagnostics.Stopwatch();
downloadTimer.Start();
StringBuilder HTMLstring = new StringBuilder("<html><head></head><body><h1>Example PDF page. Spinner in ASP.NET MVC. thoughtsonprogramming.wordpress.com</h1></body></html>");
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=" + "PDFfile.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
var configurationOptions = new PdfGenerateConfig();
configurationOptions.PageOrientation = PdfSharp.PageOrientation.Landscape;
configurationOptions.PageSize = PdfSharp.PageSize.Letter;
configurationOptions.MarginBottom = 19;
configurationOptions.MarginLeft = 2;
var output = PdfGenerator.GeneratePdf(HTMLstring.ToString(), configurationOptions);
XFont font = new XFont("Segoe UI,Open Sans, sans-serif, serif", 7);
XBrush brush = XBrushes.Black;
var outputstream = new MemoryStream();
output.Save(outputstream);
var temp = fc["spinnerToken"].ToString().Trim();
//WARNING : This is only for this example REMOVE IN REAL WORLD SCENARIO !!!!!!!!!!!!!!!!!!!!!!!
//System.Threading.Thread.Sleep(5000);
//WARNING : This is only for this example REMOVE IN REAL WORLD SCENARIO !!!!!!!!!!!!!!!!!!!!!!!
downloadTimer.Stop();
string timeTookToDownload = downloadTimer.Elapsed.Days + " Days " + downloadTimer.Elapsed.Hours + " Hours " + downloadTimer.Elapsed.Minutes + " Minutes " + downloadTimer.Elapsed.Seconds + " Seconds " + downloadTimer.Elapsed.Milliseconds + " Milliseconds ";
Session["spinnerToken"] = fc["spinnerToken"].ToString().Trim();
Session["timetooktodownload"] = timeTookToDownload;
Response.BinaryWrite(outputstream.ToArray());
return View("Index");
}
[HttpPost]
public ActionResult CheckForSpinnerSession()
{
string returnvalue = "false";
string fileDownloadTime = "false";
if (HttpContext.Session["spinnerToken"] != null)
{
returnvalue = Session["spinnerToken"].ToString();
}
if (HttpContext.Session["timetooktodownload"] != null)
{
fileDownloadTime = HttpContext.Session["timetooktodownload"].ToString();
}
return Json(new Models.DownloadStats { FileDownloadOK= returnvalue , FileDownloadTime= fileDownloadTime});
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}

Just to mention, that if you insist on using Response.End, and cannot go without it, then use cookies instead of Session State, in ASP.NET MVC or any kind of other we pages.

 

Source code on github:

https://github.com/thoughtsonprogramming/spinner

 

Video:

Fibonacci Sequence Responsive Web Template – The Golden Spiral

When we mention patterns, we come across many programming language patterns like MVC, Gang Of Four, Repository, and many others. They are called design patterns. And when used in programming, they help write and complete projects, small and big, faster than usual, because in each pattern, a system is defined, by which one can successfully come to desired results and achieve the best optiomal end-result.
Fibonacci Sequence, or golden ratio spiral,can be observed in spirals in nature.The man who observed this sequence is Fibonacci. This sequence looks something like this: 1,1,2,3,5,8 … The next number is the sum of previous two. The formula is F(n) = F(n-1) + F(n-2). And the golden ratio of dividing upper number by previous saller number is always 1.61803 approaching infinity as the numbers grow bigger.

I read a lot of materials on Fibonacci Sequence. Moreover, I was interested in its applications in real life. And nothing much is said about it.Either because it is used by many people and no one wants to tell their secret to the world, although it is not their sequence but a nature’s way of sayign “hi”. Or because indeed it is not applicable or not yet revealed how to apply it in real world. Some examples that I came across is eqyption pyramids proportions, stock market percentages. In stock market buying and selling,Fibonacci Sequence for example is integrate into graphs of rising and falling stocks,where one can make predictions on buying or selling a particular stock, after coputing Fibonacci Sequence from high and low of a trend or stock.

And I did not find much else.Oh, sorry. I also found how people, many people describe where they see the spirals in nature. That is about it.

Since I write software, I thought how can it be applied to software. Proportional web template, based on Fibonacci Sequence, is the first thing that came to my mind.If you search the web for Fibonacci Sequence, you will find the golden ratio picture drawn in squares first, with proportions (height and width) starting with 1,1,2,3,5,8 ..meaning that first two squares have width and height of 1.Then 2X2. Then 3X3. then 5×5, and so on. And with the help of Drafting Compasses, if you draw arcs from one end of each square to its opposite, and continue as the numbers increase, you get the famous Fibonacci Sequence golden spiral which you see all over the web.

So, to design Fibonacci Sequence web template, it needs to be proportional, and maintain its ratio across all screens that the page is opened. In short, it needs to be responsive. So we have width and height that we will need to base on the golden proportion of 1.61803, meaning that the total width of the screen, when bigger column width is divided by shorter column width should equal to 1.61803,no matter what the size of the screen is. Same with height.

For our case, we will consider that for segment AB, AC will be < CB, and AC is left side of a segment and CB is right segment, so AC = left = smaller part, and CB = right = bigger part.

And we will use this JavaScript code to compute left, and right segments’ width and height respectively:


function computeGoldenRatio(widthOrHeight) {
if (!widthOrHeight) {
return {};
}
return {
width: widthOrHeight,
right: Math.round(widthOrHeight/ 1.61803),
total: Math.round(widthOrHeight* 1.61803),
left: widthOrHeight- Math.round(widthOrHeight/ 1.61803)
};
}

view raw

GoldenRatio.js

hosted with ❤ by GitHub

Then we design our basic HTML table, which in theory should have two rows, and two columns, where first column of first row is also divided into two rows, as shown below:


<table width="100%" id="tblMain" bgcolor="#FFFFFF">
<tr>
<td id="col3">
<table border="1" width="100%">
<tr>
<td colspan="2" id="col33" >
<p align="center"><font style="font-size:27px"><b>(3)</b></font></td>
</tr>
<tr>
<td id="col32">
<p align="center"><font style="font-size:27px"><b>(2)</b></font></td>
<td>
<table width="100%" >
<tr>
<td id="col31">
<p align="center"><font style="font-size:27px"><b>(1)</b></font></td>
</tr>
<tr>
<td id="col311">
<p align="center"><font style="font-size:27px"><b>(1)</b></font></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td id="col5">
<p align="center"><font style="font-size:27px"><b>(5)</b></font></td>
</tr>
<tr>
<td colspan="2" id="col8">
<p align="center"><font style="font-size:27px"><b>(8)</b></font></td>
</tr>
</table>

As you can see, the numbers 1, 1, 3, 5, and 8 resectively denote and represent visually the Fibonacci Sequence proportions.

The next step, for it to be responsive, in $(document).ready or when the document or page that we are loading is ready, we will set proportions. Here is the code for that:


$(document).ready(function () {
var GRWidth = computeGoldenRatio($(window).width());
var GRHeight = computeGoldenRatio($(window).height());
$("#tblMain").height($(window).height() + "px");
$("#tblMain").width($(window).width() + "px");
$("#col3").width(GRWidth.left + "px");
$("#col5").width(GRWidth.right + "px");
$("#col3").height(GRHeight.left + "px");
var GRcol3332H = computeGoldenRatio(GRHeight.left);
$("#col33").height(GRcol3332H.right + "px");
$("#col32").height(GRcol3332H.left + "px");
$("#col31").height((GRcol3332H.left/2) + "px");
$("#col311").height((GRcol3332H.left/2) + "px");
});

So, after doing this,as you can see, wherever the page is opened, even on TV screen, it will have Fibonacci Sequence proportions, and the golden ratio among its squares.

Let’s add very basic but important styles also, in order for content inside to stay inside each cell, and color borders, to see the first Fibonacci Sequence Responsive Web Template:


<style type=text/css>
table
{
table-layout:fixed;
border:1px solid grey;
cell-padding:0px;
cell-spacing:0px;
border-collapse:collapse
}
td{
border:1px solid grey
}
</style>

Also let’s add width to 100% to the body because we want it to scale to the container’s maximum width and height, thus obtaining Golden Ratio on whatever screen or device it is opened:

<body style=”width:100%”>

And, in the head, please add JQuery script, or translate it to pure javascript if you want, but I use JQuery:

jquery-3.3.1.min.js

All the scripts are included !

Below are the pictures of emulators on major phones and devices. And as you can see, it maintains its ratio.

 

And the code for basic phase one:


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Language" content="en-ca">
<script src="jquery-3.3.1.min.js"></script>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
$(document).ready(function () {
var GRWidth = computeGoldenRatio($(window).width());
var GRHeight = computeGoldenRatio($(window).height());
$("#tblMain").height($(window).height() + "px");
$("#tblMain").width($(window).width() + "px");
$("#col3").width(GRWidth.left + "px");
$("#col5").width(GRWidth.right + "px");
$("#col3").height(GRHeight.left + "px");
var GRcol3332H = computeGoldenRatio(GRHeight.left);
$("#col33").height(GRcol3332H.right + "px");
$("#col32").height(GRcol3332H.left + "px");
$("#col31").height((GRcol3332H.left/2) + "px");
$("#col311").height((GRcol3332H.left/2) + "px");
});
function computeGoldenRatio(widthOrHeight) {
if (!widthOrHeight) {
return {};
}
return {
width: widthOrHeight,
right: Math.round(widthOrHeight/ 1.61803),
total: Math.round(widthOrHeight* 1.61803),
left: widthOrHeight- Math.round(widthOrHeight/ 1.61803)
};
}
</script>
<style type=text/css>
table
{
table-layout:fixed;
border:1px solid grey;
cell-padding:0px;
cell-spacing:0px;
border-collapse:collapse
}
td{
border:1px solid grey
}
</style>
</head>
<body style="width:100%">
<table width="100%" id="tblMain" bgcolor="#FFFFFF">
<tr>
<td id="col3">
<table border="1" width="100%">
<tr>
<td colspan="2" id="col33" >
<p align="center"><font style="font-size:27px"><b>(3)</b></font></td>
</tr>
<tr>
<td id="col32">
<p align="center"><font style="font-size:27px"><b>(2)</b></font></td>
<td>
<table width="100%" >
<tr>
<td id="col31">
<p align="center"><font style="font-size:27px"><b>(1)</b></font></td>
</tr>
<tr>
<td id="col311">
<p align="center"><font style="font-size:27px"><b>(1)</b></font></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td id="col5">
<p align="center"><font style="font-size:27px"><b>(5)</b></font></td>
</tr>
<tr>
<td colspan="2" id="col8">
<p align="center"><font style="font-size:27px"><b>(8)</b></font></td>
</tr>
</table>
</body>
</html>

The next phase or step is to include content. I decided it would be great to include bootstrap css and javascript.So we will add two bootstrap scripts:


<link rel="stylesheet" href="bootstrap.min.css">
<script src="bootstrap.min.js"></script>

Then, for each Fibonacci Square, or td, in our case, we need to add following CSS:

style=”overflow-y: scroll;text-overflow: ellipsis;white-space: nowrap;position:relative;vertical-align:top”

And, here is the important part, include all the content that we want to put inside container of any kind, (div with class = container in our case), with position absolute. This is in order to achieve respectively full elements content stretch, and overflow scroll, which is when content is higher than the square, it will add a scroll bar on Y-axis. An example would be a twitter feed in one of the suares, 1,2, 3, 5, or 8.

In short, you can put anything in those squares.

Square 2, I left it for LOGO, BRAND name, TITLE, or NAME. The rest is content, bootstrap content, whichever you want.

In reality, we added a CSS wrapper outside bootstrap, which is Fibonacci Sequence Responsive Web Template, and then inside, we have bootstrap elements.

As an example, I added the following, as shown in the pictures below:

And the code after the bootstrap was added:


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="bootstrap.min.css">
<script src="jquery-3.3.1.min.js"></script>
<script src="bootstrap.min.js"></script>
<title></title>
<script type="text/javascript">
$(document).ready(function () {
var GRWidth = computeGoldenRatio($(window).width());
var GRHeight = computeGoldenRatio($(window).height());
$("#tblMain").height($(window).height() + "px");
$("#tblMain").width($(window).width() + "px");
$("#col3").width(GRWidth.left + "px");
$("#col5").width(GRWidth.right + "px");
$("#col3").height(GRHeight.left + "px");
var GRcol3332H = computeGoldenRatio(GRHeight.left);
$("#col33").height(GRcol3332H.right + "px");
$("#col32").height(GRcol3332H.left + "px");
$("#col31").height((GRcol3332H.left/2) + "px");
$("#col311").height((GRcol3332H.left/2) + "px");
});
function computeGoldenRatio(widthOrHeight) {
if (!widthOrHeight) {
return {};
}
return {
width: widthOrHeight,
right: Math.round(widthOrHeight/ 1.61803),
total: Math.round(widthOrHeight* 1.61803),
left: widthOrHeight- Math.round(widthOrHeight/ 1.61803)
};
}
</script>
<style type="text/css">
table
{
table-layout:fixed;
border:0px solid grey;
cell-padding:0px;
cell-spacing:0px;
border-collapse:collapse
}
td{
border:0px solid grey;
white-space: nowrap;
overflow:hidden
}
</style>
</head>
<body style="width:100%">
<table width="100%" id="tblMain" bgcolor="#FFFFFF">
<tr>
<td id="col3">
<table border="1" width="100%">
<tr>
<td colspan="2" id="col33" style="overflow-y: scroll;text-overflow: ellipsis;white-space: nowrap;position:relative;vertical-align:top">
<div class="container" style="position:absolute">
<div class="list-group" style="max-width: 100%;">
<a href="#!" class="list-group-item list-group-item-action">
Lorem ipsum dolor sit amet, mollis diceret est in.</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-primary">
Ut sea periculis argumentum suscipiantur.</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-secondary">
Mei persequeris reprehendunt in, an qui malorum contentiones</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-success">
Pri hinc tamquam maiestatis te</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-danger">
Percipit legendos argumentum ut eam</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-warning">
Mutat commodo pro ad</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-info">
Ea nihil mollis definitionem sea</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-light">
Ius saepe dicunt ne, movet dicunt tamquam ei pri</a>
<a href="#!" class="list-group-item list-group-item-action list-group-item-dark">
Ex diceret placerat adipisci nec, sumo audiam platonem vix
id</a> </div>
</div>
</td>
</tr>
<tr>
<td id="col32">
<img src="phi-logo-2.png" style="width:100%;height:100%"></td>
<td>
<table width="100%">
<tr>
<td id="col31">
<button type="button" class="btn btn-info" style="width:100%;height:100%">
Sign In</button></td>
</tr>
<tr>
<td id="col311">
<button type="button" class="btn btn-info" style="width:100%;height:100%">
Sign Up</button></td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td id="col5" style="overflow-y: scroll;text-overflow: ellipsis;white-space: nowrap;position:relative;vertical-align:top">
<div class="container" style="position:absolute">
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Te adipisci salutatus duo</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary float-right">Go somewhere</a>
</div>
</div>
<div class="card border-secondary mb-3" style="max-width: 100%;">
<div class="card-header">
Lorem ipsum</div>
<div class="card-body text-secondary">
<h5 class="card-title">Pro an postea tacimates</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
</div>
</div>
<div class="card text-white bg-dark mb-3" style="max-width: 100%;">
<div class="card-header">
Lorem ipsum</div>
<div class="card-body">
<h5 class="card-title">At dictas commodo vim</h5>
<p class="card-text text-white">Lorem ipsum dolor sit amet,
mollis diceret est in. Ut sea periculis argumentum suscipiantur.</p>
</div>
</div>
<div class="card text-white bg-warning mb-3" style="max-width: 100%;">
<div class="card-header">
Lorem ipsum</div>
<div class="card-body">
<h5 class="card-title">Suas vituperatoribus sit no</h5>
<p class="card-text text-white">Lorem ipsum dolor sit amet,
mollis diceret est in. Ut sea periculis argumentum suscipiantur.</p>
</div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Assum vivendum tacimates per id</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary float-right">Go somewhere</a>
</div>
</div>
<div class="card text-white bg-primary mb-3" style="max-width: 100%;">
<div class="card-header">
Lorem ipsum</div>
<div class="card-body">
<h5 class="card-title">Primary Panel title</h5>
<p class="card-text text-white">Lorem ipsum dolor sit amet,
mollis diceret est in. Ut sea periculis argumentum suscipiantur.</p>
</div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">His quem sumo latine cu, vix quod</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary">Go somewhere</a> </div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Mei porro aperiri suavitate te</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary">Go somewhere</a> </div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Mel te volumus placerat philosophia</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary">Go somewhere</a> </div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Ea mei iisque sapientem</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary">Go somewhere</a> </div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Ea posse oblique eos</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary float-right">Go somewhere</a>
</div>
</div>
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Ea sale alienum pri</h5>
<p class="card-text">Lorem ipsum dolor sit amet, mollis diceret
est in. Ut sea periculis argumentum suscipiantur.</p>
<a href="#!" class="btn btn-primary">Go somewhere</a> </div>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2" id="col8" style="overflow-y: scroll;text-overflow: ellipsis;white-space: nowrap;position:relative;vertical-align:top">
<div class="container" style="position:absolute;max-width:100%">
<div class="card">
<h5 class="card-header h5">Lorem ipsum</h5>
<div class="card-body">
<h5 class="card-title">Data Table</h5>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">First</th>
<th scope="col">Middle</th>
<th scope="col">Last</th>
</tr>
</thead>
<tr>
<th scope="row">1</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">3</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">4</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">5</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">6</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">7</th>
<td>Fname</td>
<td>Mname</td>
<td>@logo</td>
</tr>
<tr>
<th scope="row">8</th>
<td>Fname</td>
<td>Mname</td>
<td>Lnaem</td>
</tr>
<tr>
<th scope="row">9</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">10</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">11</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">12</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">13</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">14</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">15</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">16</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">17</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
<tr>
<th scope="row">18</th>
<td>Fname</td>
<td>Mname</td>
<td>Lname</td>
</tr>
</table>
</div>
</div>
</div>
</td>
</tr>
</table>
</div>
</body>
</html>

How to generate PDF files and reports from HTML

There are many solutions that tell you how to generate PDF documents, and reports from custom HTML string, in whatever language you are coding.

The free and open source solution that works for me is PdfSharp with HtmlRenderer.

You can get PdfSharp from nuget like this:

Install-Package PdfSharp -Version 1.32.3057

And HtmlRenderer like this:

Install-Package HtmlRenderer.PdfSharp -Version 1.5.0.6

I am using ASP.NET MVC solution, so adding them both in Package Manager Console looks like this:

nuget-get-pdf-sharp

HtmlRenderer-nuget

HtmlrendererNuget-Installed

Once we installed the free packages, we head to our controller. And the actual code looks like this. Here, once the page loads, we will see the generated Pdf. In this particular case, I named my ASP.NET Solution temp01, just for this example.


using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using PdfSharp.Drawing;
using TheArtOfDev.HtmlRenderer.PdfSharp;
namespace temp01.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
//Create your HTML page that you want to print
StringBuilder HTMLContent = new StringBuilder("<!DOCTYPE html><html lang = \"en\"><head></head><body>Your Table To Print Go here</body></html>");
//Prepare the HTTP response
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=" + "MyDataReport.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
//Configure page settings
var configurationOptions = new PdfGenerateConfig();
//Page is in Landscape mode, other option is Portrait
configurationOptions.PageOrientation = PdfSharp.PageOrientation.Landscape;
//Set page type as Letter. Other options are A4 …
configurationOptions.PageSize = PdfSharp.PageSize.Letter;
//This is to fit Chrome Auto Margins when printing.Yours may be different
configurationOptions.MarginBottom = 19;
configurationOptions.MarginLeft = 2;
//The actual PDF generation
var OurPdfPage = PdfGenerator.GeneratePdf(HTMLContent.ToString(), configurationOptions);
//Setting Font for our footer
XFont font = new XFont("Segoe UI,Open Sans, sans-serif, serif", 7);
XBrush brush = XBrushes.Black;
//Loop through our generated PDF pages, one by one
for (int i = 0; i < OurPdfPage.PageCount; i++)
{
//Get each page
PdfSharp.Pdf.PdfPage page = OurPdfPage.Pages[i];
//Create rectangular area that will hold our footer – play with dimensions according to your page'scontent height and width
XRect layoutRectangle = new XRect(0/*X*/, page.Height – (font.Height + 9)/*Y*/, page.Width/*Width*/, (font.Height – 7)/*Height*/);
//Draw the footer on each page
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
gfx.DrawString(
"Page " + i + " of " + OurPdfPage.PageCount,
font,
brush,
layoutRectangle,
XStringFormats.Center);
}
}
//Prepare to download
var outputstream = new MemoryStream();
OurPdfPage.Save(outputstream);
//Write the PDF file. Browsers will trigger a popup asking to save or open a PDF file.
Response.BinaryWrite(outputstream.ToArray());
//Close the HTTP Response Stream
Response.End();
return View();
}
}
}

view raw

PdfFromHtml.cs

hosted with ❤ by GitHub

For explanation of the code above, I have summarized the comments above. This is what is happening in this order:

  1. Create your HTML page that you want to print
  2. Prepare the HTTP response
  3. Configure page settings
  4. Page is in Landscape mode, other option is Portrait
  5. Set page type as Letter. Other options are A4 …
  6. This is to fit Chrome Auto Margins when printing.Yours may be different
  7. The actual PDF generation
  8. Setting Font for our footer
  9. Loop through our generated PDF pages, one by one
  10. Get each page
  11. Create rectangular area that will hold our footer – play with dimensions according to your page’scontent height and width
  12. Draw the footer on each page
  13. Prepare to download
  14. Write the PDF file. Browsers will trigger a popup asking to save or open a PDF file.
  15. Close the HTTP Response Stream

Submitting user name and password securely

Now, since this is security cyber month, I decided to write two paragraphs on effective ways to securely transfer user name and password over the wire.

1. First Way

If you are a developer, and you have source code access to first app where user types his credentials, and also to the place where he submits them ( server ), then use simple client side encryption plus secret key on both sides ! This technique does not require HTTPS.

In the past I wondered, and maybe some of you wonder right now why to write client side encryption , for example , with JavaScript, since all this code is visible to everyone browsing the site. Well, that is why. I mean this is one of the reasons.

What happens here is follows:

Before submitting the form, you use XmlHTTP object to query back end for key, your client side encryption key. The key should be different each time, but not random. At this stage client can see the key but cannot imitate it, because it will be different each time. The algorithm to generate the key, you will store it on both sides, client, and where you are submitting your form with already client side encrypted password.

Once encryption password reaches its destination,you decrypt it there with generated new key on that side only. You do not pass the key! This is very important. The bottom line is that algorithm for generating the keys should be the same on both sides but not the keys themselves.

It is like playing around in the same domain, but in reality you are not.

It implies, that you might have to write this algorithm, and it does not have to be complex, in two different languages: Client Side and Server side, which is interesting.

The rest of the data that is not important to you, or not so important, you can hash it on client side with md5 and pass it to the server to be sure it was not tampered with along the way. First way needs a little bit coding, but very effective.

2. Second way

If you are a developer,and you DO NOT have access to the server side where the user is submitting his credentials, then you MUST use HTTPS.What this means is that ask your server administrator to enable SSL or better TLS certificates on IIS or whatever server you are using, so that the website or app will start with: https:// and not http://

Those two simple ways work, in my opinion.

Creating hyperlink [a href=”//””>Click Here ]

There are many different kinds of hyperlinks in HTML. I call the first one static which looks like so :

<a href =”https://www.domain.com”&gt; Click Here </a>

There is nothing special about this hyperlink. It is very basic, does what it is supposed to do, which is navigating to the address that you provided in href.

Next one, I call it semi-static, which navigates to exactly the page you want, like so:

<a href=”https://www.domain.com/page.html”>Click Here </a>

Next one, I call it dynamic link, because it has the ability to navigate to the same page but with different options on that page. It depends on the programmer what kind of options he or she want to show:

<a href=”https://www.domain.com/page.html?param1=123&param2=456&param3=789″>Click Here </a>

The final hyperlink that I wanted to mention is super dyamic, which looks like this:

<a href=”https://www.domain.com/api/345676/676545/876756799991/11232

The reason that I called it super dynamic is because it uses RESTful architecture and HTTP programming with GET, POST and PUT methods.But that is beyond the scope of this blog post.

Now, lets say, you have already WEB API written in C# and ASP.NET MVC. It has unique controller, and GET, POST and PUT methods in place. Meanwhile, an old website, written in classic ASP, with .asp file extension pages, needs to access our API to get full blown HTML page, and replace old page with newly designed cshtml view, by two ways:

  1. Either by clicking a link on an old ASP page and opening new cshtml view in new tab or window
  2. Or embedding our external cshtml view that our WEB API returns in an IFRAME or object in old classic ASP page.

Now, for either of the two cases, we need HREF. A link. A hyperlink. Besides, classic ASP page has different product numeric IDs, in a drop down list,  that need to be passed to WEB API to get static cshtml view, based on user selection.

So what do you think ? Which kind of hyperlink we will need ?

Obviously not static, and not semi static. We can use dynamic, but since we have WEB API C# in place, we need super dynamic hyperlink.

Now as you know, that passing pure product IDs in a query parameters is not a good idea, because someone very curious might want to try different ID instead of the one that is in our hyperlink, resulting him or her getting product that does not belong to him or her.

So, if your parameters are numerical, like 34523, or 88890889, and not strings, you could do something like this, from inside your classic ASP page:


<a target="myIframeInClassicASPPage" href="http://10.10.10.10:9988/api/values/<%=Response.Write(Server.URLEncode(Session("UserName&quot;)))%>/<%=Response.Write(698757 * Server.URLEncode(Session("product_id")))%>" >Click Here</a>

And in C# WEB API Controller, you will have a GET methos, that looks something like this:


public HttpResponseMessage Get(string u, string p)
{
Uri referrer = HttpContext.Current.Request.UrlReferrer;
dynamic myModel = new Models.MyModel();
CirculationMonitor.Controllers.HomeController hc = new HomeController();
string sd = DateTime.Now.ToShortDateString();
string ed = DateTime.Now.ToShortDateString();
var response = new HttpResponseMessage(HttpStatusCode.OK);
if (referrer != null)
{
hc.FillMysModel(myModel,(Convert.ToInt32(p) / 678957 ));
string myviewPath = HttpContext.Current.Server.MapPath(@"~/Views/Home/MyViewToReturn.cshtml");
var template = System.IO.File.ReadAllText(myviewPath);
string parsedView = RazorEngine.Razor.Parse(template, myModel);
response.Content = new StringContent(parsedView);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/html");
}
else
{
response.Content = new StringContent("<html><body><h5>Not authorized. Please use the link on ASP page to getyour product</h5></body></html>");
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/html");
}
return response;
}

https://gist.github.com/thoughtsonprogramming/b87ae47fd93a53b5ee28b7de70fbff2f.js

If you notices, 678957 is being used on classic ASP page to multiply the product ID. It is like secret or salt in md5, something similar. And, same number above is used in WEB API c# code to divide the same number to get the original ID.

So building a hyperlink is not so easy it turns out to be. However simple it looks.

Also, first code block has a tag like this: target=myIframeInClassicASPPage“. It means it targets another tag with ID myIframeInClassicASPPage, which we decided to be iFrame. So we want the external cshtml view to load in iFrame, from ASP.NET MVC WEB API C# project in IIS.

Now, in case , and just in case, because I have stumbled across such a thing, your cshtml view does not load in classic ASP, or iFrame displays weird, check your IIS HTTP headers, and explicitly set  X-UA-Compatible property in HTTP Headers section ( You will have to define new HTTP header for your website ) to IE=edge.

This is for it to be cross browser and render properly in IE. I am writing this because I am assuming you are using scripts, bootstrap, font awesome, and all other libraries that need to load with cshtml page as well. So this helps, and works brilliantly !

The end result, is that we have built a super dunamic hyperlink that renders ASP.NET MVC views in classic ASP site page’s iFrame, with all of views scripts, references, etc, using WEB API.

You never know when you will need this !

Thank you.

How to securely access remote machine or server from your ASP.NET MVC website.

We will party later on. But for now, we need to get a small word out. It is nothing special until you are confronted with it, and, in production business environment, and not at your couch! So I only have a user name and password, which is splendid ! I have user name and password for remote machine, literally. Remote machine is Windows Server 2008 R2. Now, with all the threats flying around remote accesses, password managers, which I wrote one for my self indulgence here https://thoughtsonprogramming.wordpress.com/2018/08/26/password-manager/

, and facebook “Impersonation” hacking, which just happened days ago, I had a challenge to upload a file to remote server,very far away, having user name and password.

Relying on experience, and not listening no anyone around, I took the SHARE path. Meaning I will have to access a shared folder on that remote computer, and a share that I will have to ask sys admin to enable or create a share of parent folder myself.

I remind you, it is not childish game at my home. It is production environment. Things fly by fast.

First, where my ASP.NET MVC app resides, on IIS, we need to set its application pool identity access to the user name and password of remote machine. That way, it will allow our ASP.NET app to access remote server. IIS itself will let you play around and access network classes and namespaces.

So, after we have done that. We need a  Windows Share access. It looks the same like oAuth API access, where you first obtain a token, then use that token to do authorized things you want to do. But here , you get access to the share, and IIS handles the rest, meaning it lets you copy and transfer files.

Besides, by using “Share” way to transfer files, it is convenient for sys admin on the other side to see all current and live sessions that are open concerning this particular share. So security wise, very convenient.

And we, on the other hand, open share connection, and then close it once done.

Now, I am using C#, my best language so far.

So, from C#, in ASP.NET MVC, we need to first obtain an access right to remote folder, then copy my file.

I found the network class for my dear C# language, on the internet. I use it to access remote share. Here it is:

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Web;
namespace YourApp.Helpers
{
public static class NetworkShare
{
/// <summary>
/// Connects to the remote share
/// </summary>
/// <returns>Null if successful, otherwise error message.</returns>
public static string ConnectToShare(string uri, string username, string password)
{
//Create netresource and point it at the share
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = RESOURCETYPE_DISK;
nr.lpRemoteName = uri;
//Create the share
int ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
//Check for errors
if (ret == NO_ERROR)
return null;
else
return GetError(ret);
}
/// <summary>
/// Remove the share from cache.
/// </summary>
/// <returns>Null if successful, otherwise error message.</returns>
public static string DisconnectFromShare(string uri, bool force)
{
//remove the share
int ret = WNetCancelConnection(uri, force);
//Check for errors
if (ret == NO_ERROR)
return null;
else
return GetError(ret);
}
#region P/Invoke Stuff
[DllImport("Mpr.dll")]
private static extern int WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);
[DllImport("Mpr.dll")]
private static extern int WNetCancelConnection(
string lpName,
bool fForce
);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public int dwScope = 0;
public int dwType = 0;
public int dwDisplayType = 0;
public int dwUsage = 0;
public string lpLocalName = "";
public string lpRemoteName = "";
public string lpComment = "";
public string lpProvider = "";
}
#region Consts
const int RESOURCETYPE_DISK = 0x00000001;
const int CONNECT_UPDATE_PROFILE = 0x00000001;
#endregion
#region Errors
const int NO_ERROR = 0;
const int ERROR_ACCESS_DENIED = 5;
const int ERROR_ALREADY_ASSIGNED = 85;
const int ERROR_BAD_DEVICE = 1200;
const int ERROR_BAD_NET_NAME = 67;
const int ERROR_BAD_PROVIDER = 1204;
const int ERROR_CANCELLED = 1223;
const int ERROR_EXTENDED_ERROR = 1208;
const int ERROR_INVALID_ADDRESS = 487;
const int ERROR_INVALID_PARAMETER = 87;
const int ERROR_INVALID_PASSWORD = 1216;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int ERROR_NO_NET_OR_BAD_PATH = 1203;
const int ERROR_NO_NETWORK = 1222;
const int ERROR_SESSION_CREDENTIAL_CONFLICT = 1219;
const int ERROR_BAD_PROFILE = 1206;
const int ERROR_CANNOT_OPEN_PROFILE = 1205;
const int ERROR_DEVICE_IN_USE = 2404;
const int ERROR_NOT_CONNECTED = 2250;
const int ERROR_OPEN_FILES = 2401;
private struct ErrorClass
{
public int num;
public string message;
public ErrorClass(int num, string message)
{
this.num = num;
this.message = message;
}
}
private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"),
new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"),
new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"),
new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"),
new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"),
new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"),
new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"),
new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"),
new ErrorClass(ERROR_MORE_DATA, "Error: More Data"),
new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"),
new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"),
new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"),
new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"),
new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"),
new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"),
new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"),
new ErrorClass(ERROR_SESSION_CREDENTIAL_CONFLICT, "Error: Credential Conflict"),
};
private static string GetError(int errNum)
{
foreach (ErrorClass er in ERROR_LIST)
{
if (er.num == errNum) return er.message;
}
return "Error: Unknown, " + errNum;
}
#endregion
#endregion
}
}

view raw

NetworkShare.cs

hosted with ❤ by GitHub

As you can see from the code above, we use DLLImport to obtain access to native windows DLL, which is Mpr.dll.

We could have taken the WMI option, with all of its convenient operations, which allows you to control the remote machine. But we do not want to control. We want to copy and delete a file when we need to.

So the code above only opens and closes connection to our remote machine. It does not copy files. Later we will only use standard System.IO.File.Copy(source, destination) method from C# to do the copying, once the Share connection is open.

The idea is similar to oAuth when obtaining a token, then doing something with that token, only oAuth is stateless, meaning no session is kept alive, while our method here is real time and synchroneous, where connection is open until closed.

So, after we add this to our project. and in our ASP.NET MVC, we need to call that method like so. Of-course depending on your particular needs, and the needs of an app you are using. But for me, it worked charmly as so:


//this is a comment: // Supposedly your server where you want to copy your file is: 10.10.10.10
string unme = "administrator";
string password = "Y0Ou)rS0Ec)R0Ec)Y";
NetworkShare.ConnectToShare(@"\\10.10.10.10\YourSharedFolder", unme, password); //Connect with the new credentials
//check for the file existance first
if (System.IO.File.Exists(@"\\10.10.10.10\YourSharedFolder\Subfolder" + fileName))
{
//set attributes to normal because by default files are read only, but in our case we need to delete it first
System.IO.File.SetAttributes(@"\\10.10.10.10\YourSharedFolder\Subfolder" + fileName, FileAttributes.Normal);
//if it exists delete it
System.IO.File.Delete(@"\\10.10.10.10\YourSharedFolder\Subfolder" + fileName);
//copy new file
System.IO.File.Copy(currentEmgMessagePath, @"\\10.10.10.10\YourSharedFolder\Subfolder" + fileName + "");
//close network share
NetworkShare.DisconnectFromShare(@"\\10.10.10.10\YourSharedFolder", true);
}

view raw

CopyFile.cs

hosted with ❤ by GitHub

The bottom line here is that I am not using IMPERSONATION !

First, we authenticate ourselves, the thing that I like the most, almost like in oAuth protocol, only better !  :)))))))))))

Then, once o”Authenticated”, on our “Share”, we do whatever we want.

With so much money spent on security these days, looking forward to hear some responses on whether it is safe, or not, real or not, practical or not, true or not true, well, to access remote shares in that way.

Have any other clue on doing it stuntly in a different way, please comment.

Thank you.