Log4Net is the most powerful login engine I have come across.
Logging is essential in each application>
A framework like Log4Net allows you to log to any place you want to:
File, database, etc….
In this blog I’m going to explain how to implement this engine in a simple Command Prompt project.

  1. Step 1 create a simple Command Prompt project in Visual Studio
    2. Install the package withe Nugget, just search for ‘log4net’
    3. Add a file to our project called: log4net.config
    4. Next to make this framework working we need to tell our application were to find it’s configuration, open the AssemblyInfo.cs file of your project and add the following line:
 
 [assembly: log4net.Config. XmlConfigurator(ConfigFile = "log4net.config")] 
 

Which point to our config file we just created.

  1. The Log4Net uses the concept off apenders which you could see as ‘handlers‘, this will become clear in a minute.
    5.  I’m going to use “ColoredConsoleAppenderClass
    https://logging.apache.org/log4net/release/sdk/html/T_log4net_Appender_ColoredConsoleAppender.html
 
   <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
 
  1. Each appender in the framework has mappings we need to define in our config.
    These mapping are linked to the kind of log level you are going to use:
    Warning, Error, Fatal, Info
    Let add our mappings into out config:
 <mapping>

      <level value="WARN"/>

      <foreColor value="Yellow"/>

    </mapping>

    <mapping>

      <level value="ERROR"/>

      <foreColor value="Red"/>

    </mapping>

    <mapping>

      <level value="FATAL"/>

      <foreColor value="White"/>

      <backColor value="Red" />

    </mapping>

    <mapping>

      <level value="INFO"/>

      <foreColor value="Cyan"/>

    </mapping>

    <mapping>

      <level value="DEBUG"/>

      <foreColor value="Green"/>

    </mapping>

  
  1. Next we need to determine how our log lines will look like
    In our config there is a section layout:
    <layout type="log4net.Layout.PatternLayout">

      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>

    </layout>
 
  1. Let’s wrap it up our complete config.xml:

<log4net>

  <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">

    <mapping>

      <level value="WARN"/>

      <foreColor value="Yellow"/>

    </mapping>

    <mapping>

      <level value="ERROR"/>

      <foreColor value="Red"/>

    </mapping>

    <mapping>

      <level value="FATAL"/>

      <foreColor value="White"/>

      <backColor value="Red" />

    </mapping>

    <mapping>

      <level value="INFO"/>

      <foreColor value="Cyan"/>

    </mapping>

    <mapping>

      <level value="DEBUG"/>

      <foreColor value="Green"/>

    </mapping>

    <layout type="log4net.Layout.PatternLayout">

      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>

    </layout>

  </appender>

  <root>

    <level value="DEBUG" />

    <appender-ref ref="ColoredConsoleAppender"/>

  </root>

</log4net>

 

9.Let’s see how our code look like:


using log4net;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;

namespace SBS.Blog.Log4Net
 {
 class Program
 {
 private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

static void Main(string[] args)
 {

Logger.Debug("This is a Debug statement");
 Logger.Info("This is a Info statement");
 Logger.Warn("This is a Warn statement");
 Logger.Error("This is a Error statement");
 Logger.Fatal("This is a Fatal statement");
 Console.ReadLine();
 }
 }
 }

  1. This will result in the following output:

2018-06-11 20_56_52-C__Users_Sebastian_source_repos_SBS.Blog.Log4Net_SBS.Blog.Log4Net_bin_Debug_SBS.

  1. If you want to use the EventLogAppender you could use the following configuration into our log4net.config
 <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >

       <applicationName value="MyApp" />

       <layout type="log4net.Layout.PatternLayout">

             <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />

       </layout>

</appender>

 

  1. If you want to write to a file you can use the following configuration

<appender name="FileAppender" type="log4net.Appender.FileAppender">

    <file value="${TMP}\log-file.txt" />

    <appendToFile value="true" />

    <lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />

    <layout type="log4net.Layout.PatternLayout">

        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />

    </layout>

</appender>
 

The result of the FileAppender:

loggedtofile

 

In a SharePoint MVC hosted app the ‘spcontext.js’ files has a piece of code that will append each hyperlink with the ‘SPHostUrl’ attribute.

Great….

But I ran into the fact that the script was crashing for some reason…
I added this piece

if (authority == null)
return false;

Into the file so it does not do an UpperCase on the next line because of the fact that the object is null

Modified function:

// Appends SPHostUrl as query string to all the links which point to current domain.
function appendSPHostUrlToLinks(spHostUrl, currentAuthority) {
$(“a”)
.filter(function () {
var authority = getAuthorityFromUrl(this.href);

//ADDED PIECE OF CODE
if (authority == null)

return false;

if (!authority && /^#|:/.test(this.href)) {
// Filters out anchors and urls with other unsupported protocols.
return false;
}
return authority.toUpperCase() == currentAuthority;
})
.each(function () {
if (!getSPHostUrlFromQueryString(this.search)) {
if (this.search.length > 0) {
this.search += “&” + SPHostUrlKey + “=” + spHostUrl;
}
else {
this.search = “?” + SPHostUrlKey + “=” + spHostUrl;
}
}
});
}

 

 

I use this datatable ALL the time.
https://datatables.net

For one of my projects I needed tot disable the automaticlly sorting by default.
If your page loads the datatables will sort the first column automaticlly.
To disable this, but the following in your configuration:

“aaSorting”: []

 

This is my complete configuration:

$(‘#table_timesheets_overview’).DataTable(
{
dom: ‘Bft’,
“bSort”: false,
“aaSorting”: []
});

 

Actually very straight forward if you are a SharePoint Developer:

 

public class MUACSupplierPerformanceEventReceiver : SPFeatureReceiver
{

public const string ACTION_GENERATE_REPORT = “Generate report”;

// Uncomment the method below to handle the event raised after a feature has been activated.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

RemoveAction(properties);

SPUserCustomAction useraction = spList.UserCustomActions.Add();

useraction.Name = ACTION_GENERATE_REPORT;

useraction.Location = “EditControlBlock”;

useraction.ImageUrl = “/_layouts/Images/fcoscorecard.png”;

useraction.Url = “{SiteUrl}/_layouts/EuroControl/SupplierPerformanceQuestions.aspx?itemId={ItemId}”;

useraction.Sequence = 106;

useraction.Title = ACTION_GENERATE_REPORT;

useraction.Update();

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

{

RemoveAction(properties);

}

private void RemoveAction(SPFeatureReceiverProperties properties)

{

try

{

SPSecurity.RunWithElevatedPrivileges(delegate()

{

using (SPWeb spWeb = properties.Feature.Parent as SPWeb)

{

if (spWeb.ListExists(GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION))

{

spWeb.AllowUnsafeUpdates = true;

SPList spList = spWeb.Lists[GeneralResources.ListNames.SUPPLIER_PERFORMANCE_EVALUATION];

foreach (SPUserCustomAction action in spList.UserCustomActions)

{

if (action.Title.Equals(ACTION_GENERATE_REPORT))

{

action.Delete();

spList.Update();

break;

}

}

spList.Update();

spWeb.AllowUnsafeUpdates = false;

}

}

});

}

catch (Exception ex)

{

SPTUlsLog.WriteError(ex, SPTUlsLog.Error);

}

}

}

}

 

 

The image url is ignored in SharePoint 2013, because it does not use icons.

 

Hope it helps

 

No doubt that jQuery is one of the best things to use in web development.
But when using it in SharePoint it’s a little bit tricky.
SharePoint always puts this in front of your control id: ‘ctl00$PlaceHolderMain$’ depending on which placeholder you placed your control.
When you need to access a control in jQuery in SharePoint your have to write a jQuery filter which is thinking about performance not always the best way to go.

Filter example:

$(“[id$=control_id]”)This filter will look for al controls were the id ends with your control id.
Using this filter will work perfect although writing it again and again pff…

Using this function will save you some time:

function GetSharePointNotation(control) {
return“[id$='” + control + “‘]”;
}

 

Using the function:

$(GetSharePointNotation(“control id”)).show();

 

Have fun