Technolog

Blogging over technologie.
Welcome to Technolog Sign in | Join | Help

Front Page News

  • SiteMaps made easy

     

    If you’re a site admin or asp.net developer for an internet site, you certainly need to look into sitemaps, if you want to perform SEO.

    It’s not necessary to  simply crawl your own site and then to give every page a priority, but consider this for a forum or other pages which are irregularly or often updated. If you don’t want to have crawlers do unneeded roundtrips, implement a sitemap.

    ‘robots.txt’ should contain a reference to your map eg. Sitemap: http://www.myfantasticsite.com/sitemap.xml

    Ideas of this class, written using C#, can be found anywhere on the net. However, as some might know me, I like it to be finished and neat and a self-supporting class ready for usage (e.g. it must not be written to a string to add or remove wished attributes that the serializer could not handle).

    The following things are solved. 
    Since  ‘changefreq’ and ‘lastmod’ and ‘priority’ are optional values, you don’t want the XmlSerializer to create empty tags!
    This is done by adding a DefaultValue attribute. It will cause XmlSerializer to check the current value against the default value. If they are equal, it is considered to be an empty non existing tag. Remember, that the defaultvalues need to be out of the range of possible values! Therefore, EnumChangeFreq contains an extra member ‘notused’
    An other solved issue is the xsi:schemaLocation attribute at documentelement level. You cannot add this using the XmlSerializerNamespaces class. It would simply fail on xsi:schemaLocation.

    Do not use: (XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
    ns.Add("xsi:schemaLocation", "
    http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd");)


    The solution was to serialize the XML to a XmlDocument and to append the needed attributes as seen below.
    You can use the class as follows.
    UrlSet retVal = new UrlSet();

    (simplified you can add your algorithm to retrieve the real lastmodified time)

    retVal.AddUrl(new Url() { LastModifiedDateTime = DateTime.Now.ToUniversalTime(), Loc = “http://www.myfantasticsite.com/blah.aspx”) });

    Retrieve the XML sitemap string.
    string xml = null;
    using (var io = (MemoryStream)retVal.ToStream())
    {
                    xml =  new UTF8Encoding().GetString(io.ToArray());
    }

    Since method returns a Stream object, you also can use it to directly serialize it to disk. There might be no need for roundtrips to slower string(s).

    using System;
    using System.Xml;
    using System.Xml.Serialization;
    using System.ComponentModel;
    using System.IO;

    namespace adccure
    {

        public enum EnumChangeFreq
        {
            notset,
            always,
            hourly,
            daily,
            weekly,
            monthly,
            yearly,
            never
        }
    [XmlRoot(  ElementName = "urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
    public sealed class UrlSet
        {       
            private Url[] _url;
            public void AddUrl(Url url)
            {
                int l = _url.Length + 1;
                Array.Resize(ref _url, l);
                _url[l - 1] = url;
            }

            public UrlSet()
            {
                _url = new Url[0];
            }
            [XmlElement(ElementName = "url")]
            public Url[] url
            {
                get { return _url; }
                set { _url = value; } //bogus
            }
            /// <summary>
            /// serializes the UrlSet to a sitemap.xsd conform string ready for saving to disk.
            /// </summary>
            /// <returns>a Stream object</returns>
            public Stream ToStream()
            {
                XmlSerializer xmlser = new XmlSerializer(this.GetType());

                XmlDocument doc = new XmlDocument();
                var io = new MemoryStream();
                xmlser.Serialize(io, this);
                io.Position = 0;
                doc.Load(io);
                var attr = doc.CreateAttribute("schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
                attr.Value = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd";
                doc.DocumentElement.Attributes.Append(attr);
                io.Position = 0;
                doc.Save(io);
                io.Position = 0;
                return io;
            }
        }

        public sealed class Url
        {
            private string _loc;
            private DateTime _lastmod;
            private float _priority;
            private EnumChangeFreq _changefreq;

            public Url()
            {
                //setting defaults
                _changefreq = EnumChangeFreq.notset;
                _priority = 0.0F;
                _lastmod = DateTime.MinValue;
            }

            [XmlElement(ElementName = "loc")]
            public string Loc
            {
                get
                {
                    return _loc;
                }

                set
                {
                    if (string.IsNullOrEmpty(value))
                    {
                        throw new ArgumentNullException();
                    }
                    if (value.Length < 12 || value.Length > 2048)
                    {
                        throw new ArgumentException("loc must be between 12 and 2048 in length");
                    }
                    _loc = value;
                }
            }
            [XmlElement(ElementName = "lastmod"), DefaultValue(typeof(DateTime), "1-1-0001")]
            public DateTime LastModifiedDateTime
            {
                get
                {
                    return _lastmod;
                }

                set
                {
                    _lastmod = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Kind);

                }
            }
            [XmlElement(ElementName = "changefreq")]
            [DefaultValue(EnumChangeFreq.notset)]
            public EnumChangeFreq ChangeFreq
            {
                get
                {
                    return _changefreq;
                }

                set
                {
                    _changefreq = value;
                }
            }
            [XmlElement(ElementName = "priority")]
            [DefaultValue(0.0F)]
            public float Priority
            {
                get
                {
                    return _priority;
                }

                set
                {
                    if (value < 0 || value > 1.0)
                    {
                        throw new ArgumentException("Must be between 0 and 1");
                    }
                    _priority = value;
                }
            }
        }
    }

    Tags van Technorati: ,,
    01-31-2010, 18:12 by eprogrammer to eprogrammer
  • Howto: Create a custom numeric pager for the ASP.NET Gridview Control

    Figure 1: Our custom pager in action!

    I never have liked the concept of storing all the data in whatever form (DataTable/Lists of records/etc.) to the ASP.NET gridview control and having it automatically manage paging for me. This could however be improved using Visual Studio 2008 wizards. However, this requires writing stored procedures.

    I’ve got a concept for you, which works without a lot of extra work. The concept is:

    1. Inherit from asp:GridView and override the PageCount and the PageIndex properties
    2. Create in instance of my CustomPager class at DataBinding time.

    The result is as shown in figure 1: It adheres to PageButtonCount, to any styles that you have applied to the GridView and it features a ‘jump to’ page input box.
    The ‘native’ event handling in your ASPX, still can be maintained by this code since it emulates the PageIndexChanging event.

    Other solutions, implement an AJAX updatepanel per row. This really minimizes unnecessary refreshing of grid data.
    However, I don’t mind if say, 30 rows are being pulled from a DB and bound to a GridView. If we do so, we also get the best of two worlds, in one world, we get all data and have a fresh update of the real table rows, and in the other world, we have just the active row being fetched and we could end up having outdated data on our screen because of (for instance) co-writers/typers who updated rows at the database which are not reflected at our screen in grid (which is displayed using a gridview).

    So, in other words, I like this pager control since it is a balanced solution! It has been tested on 10,000 records. WHen I would page to for instance, page 900, it really is a matter of fractions of a second to get a response. (For paging solutions  on SQL server data tables which contain say millions of rows, we would need a more sophisticated approach).

    Here is the control source (it’s called gridhelper.cs)! (In theory, it should work for .NET 2.0 up to 3.5)

    // if you use this code, please leave the original author
    //author: Egbert Nierop
    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    namespace adccure.tools
    {
        public sealed class GridView2 : GridView
        {
            public GridView2()
                : base()
            {
            }
            private int _pageCount;

            public override int PageIndex
            {
                get
                {
                    object o = ViewState["pgi"];
                    return o == null ? 0 : (int)o;
                }
                set
                {
                    ViewState["pgi"] = value;
                }
            }

            public override int PageCount
            {
                get
                {
                    return _pageCount;
                }

            }
            public void PageCountSet(int pageCount)
            {
                _pageCount = pageCount;
            }
        }

        public sealed class CustomPager : ITemplate
        {
            readonly Table tbl;
            readonly GridView2 _grid;

            //readonly int _totalPages;
            public CustomPager(GridView2 grid, int totalPages)
            {
                tbl = new Table();
                grid.PageCountSet(totalPages);
                tbl.Width = Unit.Percentage(100);
                _grid = grid;
            }
            void ITemplate.InstantiateIn(Control container)
            {
                container.Controls.Add(tbl);

                int pageSize = _grid.PageSize;
                int pageIndex = _grid.PageIndex;
                int pageButtonCount = _grid.PagerSettings.PageButtonCount;
                int pageCount = _grid.PageCount;
                ClientScriptManager cs = _grid.Page.ClientScript;
                _grid.PagerSettings.Visible = true;
                var trow = new TableRow();
                var trowpagePosition = new TableRow();
                tbl.Rows.Add(trow);
                tbl.Rows.Add(trowpagePosition);
                TextBox tb = new TextBox();
                tb.ID = "txtJumpPage";
                tb.MaxLength = 4;
                tb.Width = Unit.Pixel(40);
                tb.Text = (pageIndex + 1).ToString();
                //avoid bubble up by return false
                tb.Attributes["onkeydown"] = string.Format("if (event.keyCode==13) {__doPostBack('{0}', 'Page$' + this.value ); return false;}", _grid.UniqueID);

                LiteralControl lit = new LiteralControl(string.Format(" of {0}", (pageCount + 1).ToString()));
                TableCell posCaption = new TableCell();
                trowpagePosition.Cells.Add(posCaption);
                posCaption.Controls.Add(tb);
                posCaption.Controls.Add(lit);
                int cellspan = 0;
                if (pageIndex > 0)
                {
                    var cellText = new TableCell();
                    trow.Cells.Add(cellText);
                    cellspan++;
                    cellText.Controls.Add(new HyperLink()
                    {
                        NavigateUrl = cs.GetPostBackClientHyperlink(_grid,
                        string.Format("Page${0}", pageIndex - pageButtonCount >= 0 ? (pageIndex - pageButtonCount) + 1 : 1), false),
                        Text = "<"
                    });
                }
                for (int x = pageIndex; x < pageIndex + pageButtonCount && x <= pageCount; x++)
                {
                    var cellText = new TableCell();
                    cellspan++;
                    trow.Cells.Add(cellText);
                    cellText.Controls.Add(new HyperLink()
                    {
                        NavigateUrl = cs.GetPostBackClientHyperlink(_grid,
                            string.Format("Page${0}", x + 1), false),
                        Text = (x + 1).ToString(),
                    });

                }
                if (pageIndex + pageButtonCount < pageCount)
                {
                    var cellText = new TableCell();
                    cellspan++;
                    trow.Cells.Add(cellText);

                    cellText.Controls.Add(new HyperLink()
                    {
                        NavigateUrl = cs.GetPostBackClientHyperlink(_grid,
                        string.Format("Page${0}", (pageIndex + pageButtonCount) + 1), false),
                        Text = ">"
                    });
                }
                tbl.Visible = true;
                posCaption.HorizontalAlign = HorizontalAlign.Center;
                posCaption.ColumnSpan = cellspan;
            }
        }
    }

    Now, I don’t publish the code to read from sample data (such as northwind), it would be a yadda, yadda and you know the drill. (In my code, it is just a silly HttpReferrer table having all the columns which allow you research this specific statistical interest of your web site)
    But in my datalayer, I have things like shown below. It is a great solution for those tables, for say, less than 100,000 records. SQL server is able to deal with these types of queries pretty well and we still avoid pumping around lots of redundant data on the network and gridview control.

    public IList<HttpReferrer> getHttpReferrers(int pPage, int pPageSize, HttpReferrerSortOrder sortOrder,
                    SortDirection sortDirection,
                    out int totalRecords)
            {
                totalRecords = dcd.HttpReferrers.Count();
                IQueryable<HttpReferrer> retVal = null;
                if (sortDirection ==  SortDirection.Ascending)
                {
                    switch (sortOrder)
                    {
                        case HttpReferrerSortOrder.Referer:
                            retVal = dcd.HttpReferrers.OrderBy(t => t.Referrer);
                            break;
                        case HttpReferrerSortOrder.IP:
                            retVal = dcd.HttpReferrers.OrderBy(t => t.IP_Address);
                            break;
                        case HttpReferrerSortOrder.Page:
                            retVal = dcd.HttpReferrers.OrderBy(t => t.page);
                            break;
                        default:
                            retVal = dcd.HttpReferrers.OrderBy(t => t.ts);
                            break;
                    }
                }
                else
                {
                    switch (sortOrder)
                    {
    case HttpReferrerSortOrder.Referer:
                            retVal = dcd.HttpReferrers.OrderByDescending(t => t.Referrer);
                            break;
                        case HttpReferrerSortOrder.IP:
                            retVal = dcd.HttpReferrers.OrderByDescending(t => t.IP_Address);
                            break;
                        case HttpReferrerSortOrder.Page:
                            retVal = dcd.HttpReferrers.OrderByDescending(t => t.page);
                            break;
                        default:
                            retVal = dcd.HttpReferrers.OrderByDescending(t => t.ts);
                            break;
                    }
                }
                return retVal.Skip(pPage * pPageSize).Take(pPageSize).ToList();
            }

    So, our Grid, can sort and it can page.

    How do we deal with the paging event at the code behind the aspx?
    It’s so simple!

    void gridHttpReferrers_PageIndexChanging(object sender, GridViewPageEventArgs e) 

    gridHttpReferrers.PageIndex = e.NewPageIndex; 
    gridHttpReferrers.DataBind(); 
    }

    void gridHttpReferrers_DataBinding(object sender, EventArgs e)

    {   int totalRecords; 
       int pageSize = gridHttpReferrers.PageSize; 
       int pageIndex = gridHttpReferrers.PageIndex; 
      gridHttpReferrers.DataSource = datadal.getHttpReferrers(pageIndex, pageSize, SortOrder, SortDir, out totalRecords); 
       gridHttpReferrers.PagerTemplate = new CustomPager(gridHttpReferrers, totalRecords); 
       gridHttpReferrers.PageCountSet (totalRecords / pageSize);
    }

    What did I do to get the new grid behavior inside the ASPX?
    Just rename the tag from asp:GridView to ctrl:GridView2 and create the reference (or in web.config)

    <%@ Register TagPrefix="ctrl" Namespace="adccure.tools" %>

    <ctrl:GridView2 runat="server" emptydatatext="No data available."  ID="gridHttpReferrers" AllowPaging="true" AllowSorting="True"  Width ="100%" AutoGenerateColumns="false" PageSize="20" DataKeyNames="ID"><PagerSettings Position="Top" PageButtonCount="20" />
    <
    Columns> ETC.

    So, I hope this code was quite enlightning for you and you can play with it and have fun.

    10-28-2009, 21:51 by eprogrammer to eprogrammer
    Filed under: , ,
  • Howto: Check the case sensitive existence of a file

    Sometimes, especially for files running on external FTP servers, where file names are case sensitive, a file named myFILE.html, is not the same file as myfile.html in the same path!

    This function, can be used on an NTFS path for that purpose where File.Exists would fail, because it is case insensitive. (However, it does not enable you to have two files with just a different case in the same path)

    /// <summary>
    /// Checks existance of file using a case sensitive compare
    /// </summary>
    /// <param name="file">must be full filename</param>
    /// <returns></returns>
    static bool FileExists(string file)

    {

    string pathCheck = Path.GetDirectoryName(file);

    string filePart = Path.GetFileName(file);

          if (string.IsNullOrEmpty(pathCheck))

          {

          throw new ArgumentException("The file must include a full path", file);

          }

          string[] checkFiles = Directory.GetFiles(pathCheck, filePart, SearchOption.TopDirectoryOnly);

          if (checkFiles != null && checkFiles.Length > 0)

          {

                //must be a binary compare

                return Path.GetFileName(checkFiles[0]) == filePart;

           }

           return false;

    }

     

    07-06-2009, 11:03 by eprogrammer to eprogrammer
    Filed under: , ,
  • Howto: Properly use the AccessCheck API for the current user

    I've seen people pulling the hair out for not getting this API workign for them.

    The API, even if impersonating the current user, returns error 1309. "An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client."

    The clue is that this API, (and this is not clearly documented on the MSDN) needs a duplicated handle.

    Anyway, spare your hair, have fun with the code. B.t.w. You can hire me for smart code and research on components etc..

    http://www.adccure.nl for contact.

        [StructLayout(LayoutKind.Sequential)]

        internal struct GENERIC_MAPPING

        {

            internal uint GenericRead;

            internal uint GenericWrite;

            internal uint GenericExecute;

            internal uint GenericAll;

        }

        [DllImport("advapi32.dll", SetLastError = false)]

        static extern void MapGenericMask([In, MarshalAs(UnmanagedType.U4)] ref TokenAccessLevels AccessMask,

            [In] ref GENERIC_MAPPING map);

       

        [DllImport("advapi32.dll", SetLastError = true)]

        [return: MarshalAs(UnmanagedType.Bool)]

        public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,

                [MarshalAs(UnmanagedType.U4)] TokenImpersonationLevel level,

                out int DuplicateTokenHandle);

        [DllImport("advapi32.dll", SetLastError = true)]       

         [return: MarshalAs(UnmanagedType.Bool)] static extern bool AccessCheck(

          [MarshalAs(UnmanagedType.LPArray)]

            byte[] pSecurityDescriptor, 

          IntPtr ClientToken,

          [MarshalAs(UnmanagedType.U4)]

            TokenAccessLevels accessmask,

          [In] ref GENERIC_MAPPING GenericMapping,

          IntPtr PrivilegeSet,

          ref int PrivilegeSetLength,

          out uint GrantedAccess,

          [MarshalAs(UnmanagedType.Bool)]

          out bool AccessStatus);

        [DllImport("kernel32")]

        static extern void CloseHandle(IntPtr ptr);

      internal static bool hasReadAccess(string path)

        {

            // Obtain the authenticated user's Identity

           

            WindowsIdentity winId = WindowsIdentity.GetCurrent(TokenAccessLevels.Duplicate | TokenAccessLevels.Query);

           

            WindowsImpersonationContext ctx = null;

            int statError = 0;

            IntPtr dupToken = IntPtr.Zero;

            try

            {

                // Start impersonating

                //ctx = winId.Impersonate(); works but AccessCheck does not like this

               

                int outPtr;

                //AccessCheck needs a duplicated token!

                DuplicateToken(winId.Token, TokenImpersonationLevel.Impersonation, out outPtr);

               

                dupToken = new IntPtr(outPtr);

                ctx = WindowsIdentity.Impersonate(dupToken);                

                Folder.GENERIC_MAPPING map = new Folder.GENERIC_MAPPING();

                map.GenericRead = 0x80000000;

                map.GenericWrite = 0x40000000;

                map.GenericExecute = 0x20000000;

                map.GenericAll = 0x10000000;

                TokenAccessLevels required = TokenAccessLevels.Query | TokenAccessLevels.Read | TokenAccessLevels.AssignPrimary | (TokenAccessLevels)0x00100000; // add synchronization

                MapGenericMask(ref required, ref map);

               

               

                uint status = 0;

                bool accesStatus = false;

                // dummy area the size should be 20 we don't do anything with it

                int sizeps = 20;

                IntPtr ps = Marshal.AllocCoTaskMem(sizeps);

               

                //AccessControlSections.Owner | AccessControlSections.Group MUST be included,

                //otherwise the descriptor would be seen with ERROR 1338

                var ACE = Directory.GetAccessControl(path,

                    AccessControlSections.Access | AccessControlSections.Owner |

                        AccessControlSections.Group);

               

                bool success = AccessCheck(ACE.GetSecurityDescriptorBinaryForm(), dupToken, required, ref map,

                        ps, ref sizeps, out status, out accesStatus);

                Marshal.FreeCoTaskMem(ps);

                if (!success)

                {

                    statError = Marshal.GetLastWin32Error();

                }

                else

                {

                    return accesStatus;

                }

            }

            // Prevent exceptions from propagating

            catch (Exception ex)

            {

                Trace.Write(ex.Message);

            }

            finally

            {

                // Revert impersonation

               

                if (ctx != null)

                    ctx.Undo();

                CloseHandle(dupToken);

            }

            if (statError != 0)

            {

                throw new Win32Exception(statError);

            }

           

            return false;

        }

    This code is just a cut and paste. You can make it pretty.

    06-18-2009, 23:50 by eprogrammer to eprogrammer
    Filed under: , , ,
  • Fill a dropdownlist using the current UICulture in (ASP.NET)

    Just for educational purpose (as for myself as well :) ) I post this code.

    Using this, a programmer can use a good practise, that is to use the culture information which is built in, into .NET instead of making that data him/herself.

    based upon the following element in web.Config, the list will fill using the correct number of monthnames. It also keeps track of calendars, that have 13 months in some cultures.

    <globalization uiCulture="nl-nl"/>

    using System;

    using System.Collections.Generic;

    using System.Web;

    using System.Linq;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Threading;

    using System.Globalization;

    public partial class _Default : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

            int monthNumber = 0;

            CultureInfo ci = Thread.CurrentThread.CurrentUICulture;

            var myMonthnames = ci.DateTimeFormat.MonthNames

                .Take(ci.Calendar.GetMonthsInYear(DateTime.Today.Year)).Select(p => new { monthNo = ++monthNumber, monthName = p });

            ddlMonthnames.DataTextField = "monthName";

            ddlMonthnames.DataValueField = "monthNo";

            ddlMonthnames.DataSource = myMonthnames;

            ddlMonthnames.DataBind();

        }

    }

    <select name="ddlMonthnames" id="ddlMonthnames">

    <option value="1">januari</option>

    <option value="2">februari</option>

    <option value="3">maart</option>

    <option value="4">april</option>

    <option value="5">mei</option>

    <option value="6">juni</option>

    <option value="7">juli</option>

    <option value="8">augustus</option>

    <option value="9">september</option>

    <option value="10">oktober</option>

    <option value="11">november</option>

    <option value="12">december</option>

    </select>

    06-18-2009, 12:05 by eprogrammer to eprogrammer
    Filed under: , ,

Who is Online

There are 2 guest(s) online. Currently there are no online users.

Technolog is vernieuwd

Wil je ook een weblog? Meld je dan gratis aan.

Technorati Zoeken

Recent Additions

  • Sunset in Seattle
Powered by Community Server, by Telligent Systems