مطالب
آموزش فایرباگ - #6 - HTML Panel - Side Panels
در قسمت قبل توضیحاتی در مورد تب HTML ارائه کردیم.
در این قسمت توضیحات کاملی در مورد پنل‌های جانبیِ داخل پنل HTML می‌دهیم.



Side Panels

در پنل HTML درکنار ارائه امکاناتی برای مشاهده و کار با تگ‌های صفحه ، اطلاعات و امکانات دیگری هم برای تگ انتخاب شده در قسمت NodeView وجود دارد.
این امکانات در پنل هایی که سمت راست پنل اصلی قرار دارند گنجانده شده است که به ترتیب برای نمایش و ویرایش استایل‌ها ، مشاهده استایل‌های محاسبه شده ، مشاهده Layout یا آرایش و نمایش اطلاعات DOM تگ انتخاب شده در NodeView هستند.




1 - Style
در این تب استایل هایی که در حال حاظر بروی تگ انتخاب شده اعمال شده اند ، نمایش داده می‌شود.
در صورتی که موس را بروی مقادیر استایل هایی که جلوه‌ی بصری دارند بگیرید ، یک پاپ‌آپ کوچک نمایان می‌شود که مقدار را نمایش می‌دهد.


 Options Menu

هر تب یا پنل در فایرباگ دارای یک سری تنظیمات است که Options Menu نام دارد. تب Style هم دارای یک سری تنظیمات است که دانشتن آنها بسیار به شما کمک خواهد کرد.
این منو با کلیک کردن بروی فلش تب () یا راست کلیک کردن بروی تب ظاهر می‌شود.


  • Only Show Applied Styles
    در صورت انتخاب ، فقط استایل هایی که اعمال شده اند نمایش داده می‌شوند. (استایل‌های Overwrite شده نمایش داده نمی‌شوند.)
    (این گزینه قابلیت خوبی است ، اما چندبار برای بنده پیش آمده که این مورد به اشتباه استایلی که اعمال شده بود را هم Overwrite شده در نظر گرفته بود. پس در هین طراحی استایل و کار با CSS اگر احیانا یکی از استایل هایتان وجود نداشت و از وجود آن اطمینان داشتید ، غیرفعال کردن این گزینه را امتحان کنید.)

  • Show User Agent CSS
    با فعال کردن این گزینه ، استایل هایی که توسط مرورگر اعمال شده اند هم نمایش داده می‌شوند.

  • Expand Shorthand Properties
    با فعال کردن این گزینه ، استایل هایی که بصورت کوتاه شده تعریف شده اند را بصورت گسترده و باز شده نمایش می‌دهد.
    برای مثال ، دستور margin را بصورت margin-top , margin-right , margin-bottom , margin-left نمایش می‌دهد.

  • سه گزینه ی Colors As Hex ، Colors As RGB و Colors As HSL تعیین کننده‌ی فرمت نمایش رنگ‌ها هستند.

  • سه گزینه ی :active ، :hover و :focus هم برای تعیین کلاس کاذب برای تگ جاری کاربرد دارند.
    برای مثال شما می‌خواهید استایلی که یک لینک زمان موس برویش قرار دارد را بررسی کنید ، لینک را در NodeView انتخاب می‌کنید و سپس از گزینه‌ی :hover را فعال می‌کنید.


Panel

  • Element styles
    استایل هایی که بصورت inline (در خود تگ) تعریف شده اند هم در این قسمت نمایش داده می‌شود و نام rule آن element.style است.



  • Source Links
    در بالا-راست هر بخش ، یک لینک قرار دارد که لینک فایل استایلی است که در همان قسمت وجود دارد و عددی که در پرانتز قرار دارد ، شماره خط استایل در همان فایل است.
    اگر نام فایل با نام صفحه‌ی جاری برابر باشد ، به معنی وجود استایل در تگ <style> در صفحه‌ی جاری است و شماره‌ی بعد از # هم ایندکس تگ <style> است.
    (با کلیک بروی لینک فایل ، فایل در خط مورد نظر در پنل CSS نمایش داده می‌شود.)



  • Inherited rules
    rule‌های به ارث رسیده هم در قسمت‌های جداگانه به همراه استایل‌های به ارث رسیده نمایش داده می‌شود و تگی والد که استایل‌ها از آن به ارث رسیده اند هم در قسمت عنوان همان استایل‌ها نمایش قرار داده شده است. (با کلیک بروی آن ، در قسمت Nodeview انتخاب می‌شود.)



  • User agent rules
    استایل هایی که توسط مرورگر اعمال شده اند (User agent rules) ، با عبارت <System> در زیر لینک منبع استایل ، مشخص شده اند.



  • Overwritten styles
    استایل‌های overwrite شده ، با یک خط برویشان مشخص شده اند.



  • Inline editing
    استایل‌های نمایش داده شده در این پنل را براحتی و با کلیک کردن بروی نام یا مقدار هر یک از دستورات می‌توانید تغییر دهید.
    برای نوشتن دستورات و مقادیر آن‌ها می‌توانید از پیشنهاد‌های فایرباگ هم کمک بگیرید و با دکمه‌های Arrow Up و Arrow Down هم بین مقادیر مجاز حرکت کنید.
    دستورات یا مقادیر نا صحیح در هین تایپ ، با رنگ قرمز و مقادیر صحیح با رنگ سبز مشخص می‌شوند.
    (این امکان خیلی مفید است ، برای مثال می‌خواهید فونت‌های مختلف را برای یک استایل امتحان کنید ، دستور font-family را می‌نویسید و بعد از زدن Enter ، با دکمه های Arrow Up و Arrow Down در لحظه نتیجه‌ی اعمال فونت‌های مختلف و دردسترس را مشاهده می‌کنید و بهترین را بر می‌گزینید.
    یا برای یافتن بهترین مقدار margin ، بعد از دستور margin ، زدن کلید Enter ، وارد کردن یک عدد برای شروع ، می‌توان باز هم با دکمه های Arrow Up و Arrow Down به سرعت تغییر را در صفحه مشاهده کرد.)

  • Rendered font highlighted
    برای دستور font ، فایرباگ هوشمندانه عمل کرده و فونتی که در حال استفاده است را پررنگ می‌کند.
    این امکان برای یافتن خطاهای متداول هنگام تعریف فونت‌های غیر سیستمی ، بسیار مفید است.




 Context Menu

این منو زمانی که در پنل راست کلیک کنید ظاهر می‌شود و نسبت به منطقه (Context)ای که در آن راست کلیک کرده اید ، گزینه‌های متفاوتی را مشاهده خواهید کرد. در جدول زیر ، گزینه‌ها ، Contextشان و توضیح هر گزینه آمده است.


 گزینه Context توضیحات 
Copy Rule Declaration CSS selector  CSS Rule فعلی را به همراه استایل هایش در clipboard کپی می‌کند
Copy Style Declaration CSS selector  استایل‌های CSS Rule فعلی را در clipboard کپی می‌کند 
Copy Location source link  آدرس فایل تعریف CSS Rule را در clipboard کپی می‌کند
Open in New Tab source link   آدرس فایل تعریف CSS Rule را در تب جدید باز می‌کند
Edit Element Style... everywhere  امکان تعریف استایل‌های درون تگ (inline) را محیا می‌کند
Add Rule... everywhere  یک Rule جدید ایجاد می‌کند
CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد می‌دهد
Delete "<rule name>" CSS selector  CSS Rule فعلی را حذف می‌کند
New Property... CSS rule  یک استایل جدید به CSS Rule فعلی اضافه می‌کند
Edit "<property name>"... CSS property  Property فعلی را به حالت ویرایش می‌برد
راه دیگر ویرایش Property ، کلیک بروی آن است
Delete "<property name>" CSS property  Property فعلی را حذف می‌کند
Disable "<property name>" CSS property  Property فعلی را غیر فعال می‌کند
را سریع‌تر ، کلیک کردن در ناحیه‌ی پشت Property ، بروی علامت قرمز رنگ است
Refresh everywhere  محتویات پنل را بروز می‌کند
Inspect in DOM Panel
CSS rule  CSS Rule فعلی را در پنل DOM برای بررسی باز می‌کند
Inspect in CSS Panel CSS rule   CSS Rule فعلی را در پنل CSS برای بررسی باز می‌کند
<Default Editor Name> CSS rule  فایل تعریف استایل‌ها را در ادیتور تعریف شده باز می‌کند
(این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد)


2 - Computed

دراین تب نتیجه‌ی پردازش استایل‌های ارائه شده توسط کاربر ، برای تگ مشخص شده در قسمت NodeView نمایش داده می‌شود. (مقادیر استایل هایی که در نهایت بروی تگ اعمال شده اند.)


Style Tracing
برای ردیابی استایل‌ها ، استایل‌ها به ترتیب اعمال شدنشان مرتب شده اند و اولین مقدار ، مقداری است که اعمال شده است.
مقادیر Overwrite بصورت خط کشیده شده و استایل‌های Overwrite شده بصورت خاکستری-کمرنگ نمایش داده می‌شوند.
هر استایل هم مانند تب Style ، یک لینک به منبع خود دارد.


Options Menu

  • Show User Agent CSS
    در صورت انتخاب ، فقط استایل هایی که اعمال شده اند نمایش داده می‌شوند.

  • Sort alphabetically
    در صورت انتخاب ، استایل‌ها به ترتیب الفبا ، و درصورت عدم انتخاب بصورت گروه بندی نمایش داده می‌شوند.

  • Show Mozilla Specific Styles
    در صورت انتخاب ، استایل‌های مخصوص Mozilla را نمایش می‌دهد. (استایل هایی با پیشوند -moz-)

  • سه گزینه‌ی Colors As Hex ، Colors As RGB و Colors As HSL تعیین کننده‌ی فرمت نمایش رنگ‌ها هستند.


Context Menu

این منو زمانی که در پنل راست کلیک کنید ظاهر می‌شود و نسبت به منطقه (Context)ای که در آن راست کلیک کرده اید ، گزینه‌های متفاوتی را مشاهده خواهید کرد. در جدول زیر ، گزینه‌ها ، Contextشان و توضیح هر گزینه آمده است.

 گزینه Context توضیحات 
Expand All Styles everywhere  CSS Rule فعلی را به همراه استایل هایش در clipboard کپی می‌کند
Collapse All Styles everywhere  استایل‌های CSS Rule فعلی را در clipboard کپی می‌کند 
Inspect in DOM panel styles   آدرس فایل تعریف CSS Rule را در تب جدید باز می‌کند
Copy Location
style source link  امکان تعریف استایل‌های درون تگ (inline) را محیا می‌کند
Open in New Tab style source link  یک Rule جدید ایجاد می‌کند
CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد می‌دهد
Inspect in CSS panel style source link  CSS Rule فعلی را حذف می‌کند
<Default Editor Name> style source link  فایل تعریف استایل‌ها را در ادیتور تعریف شده باز می‌کند
(این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد)


3 - Layout

در این تب ، مقادیر Box Model بصورت بصری نمایش می‌دهد. می‌توان با کلیک کردن بروی هریک از مقادیر ، آن را ویرایش کرد. (این تغییر بصورت inline در تگ اعمال می‌شود.)
با حرکت موس بروی قسمت‌های مختلف ، می‌توان همان قسمت‌ها را در صفحه بصورت خط کشی شده مشاهده کرد.
(البته ظاهرا در ورژن 1.10.4 که بنده استفاده می‌کنم ، عملیات ویرایش مقادیر به درستی انجام نمی‌شود.)



 Options Menu

  • Show Rulers and Guides
    در صورت انتخاب ، خط‌های راهنما را هنگام حرکت موس بروی اجزای Box Model در صفحه نمایش می‌دهد.




4 - DOM

این پنل اطلاعات DOM تگ جاری را نمایش می‌دهد.
این پنل تمام قابلیت‌های پنل DOM اصلی را دارا می‌باشد.
(در مقالات آینده با تب DOM آشنا خواهیم شد.)



مطالب
پیاده سازی Password Policy سفارشی توسط ASP.NET Identity
برای فراهم کردن یک تجربه کاربری ایمن‌تر و بهتر، ممکن است بخواهید پیچیدگی password policy را سفارشی سازی کنید. مثلا ممکن است بخواهید حداقل تعداد کاراکتر‌ها را تنظیم کنید، استفاده از چند حروف ویژه را اجباری کنید،  جلوگیری از استفاده نام کاربر در کلمه عبور و غیره. برای اطلاعات بیشتر درباره سیاست‌های کلمه عبور به این لینک مراجعه کنید. بصورت پیش فرض ASP.NET Identity کاربران را وادار می‌کند تا کلمه‌های عبوری بطول حداقل 6 کاراکتر وارد نمایند. در ادامه نحوه افزودن چند خط مشی دیگر را هم بررسی می‌کنیم.

با استفاده از ویژوال استودیو 2013 پروژه جدیدی خواهیم ساخت تا از ASP.NET Identity استفاده کند. مواردی که درباره کلمه‌های عبور می‌خواهیم اعمال کنیم در زیر لیست شده اند.

  • تنظیمات پیش فرض باید تغییر کنند تا کلمات عبور حداقل 10 کاراکتر باشند
  • کلمه عبور حداقل یک عدد و یک کاراکتر ویژه باید داشته باشد
  • امکان استفاده از 5 کلمه عبور اخیری که ثبت شده وجود ندارد
در آخر اپلیکیشن را اجرا می‌کنیم و عملکرد این قوانین جدید را بررسی خواهیم کرد.


ایجاد اپلیکیشن جدید

در Visual Studio 2013 اپلیکیشن جدیدی از نوع ASP.NET MVC 4.5 بسازید.

در پنجره Solution Explorer روی نام پروژه کلیک راست کنید و گزینه Manage NuGet Packages را انتخاب کنید. به قسمت Update بروید و تمام انتشارات جدید را در صورت وجود نصب کنید.

بگذارید تا به روند کلی ایجاد کاربران جدید در اپلیکیشن نگاهی بیاندازیم. این به ما در شناسایی نیازهای جدیدمان کمک می‌کند. در پوشه Controllers فایلی بنام AccountController.cs وجود دارد که حاوی متدهایی برای مدیریت کاربران است.

  • کنترلر Account از کلاس UserManager استفاده می‌کند که در فریم ورک Identity تعریف شده است. این کلاس به نوبه خود از کلاس دیگری بنام UserStore استفاده می‌کند که برای دسترسی و مدیریت داده‌های کاربران استفاده می‌شود. در مثال ما این کلاس از Entity Framework استفاده می‌کند که پیاده سازی پیش فرض است.
  • متد Register POST یک کاربر جدید می‌سازد. متد CreateAsync به طبع متد 'ValidateAsync' را روی خاصیت PasswordValidator فراخوانی می‌کند تا کلمه عبور دریافتی اعتبارسنجی شود.
var user = new ApplicationUser() { UserName = model.UserName };  
var result = await UserManager.CreateAsync(user, model.Password);
  
if (result.Succeeded)
{
    await SignInAsync(user, isPersistent: false);  
    return RedirectToAction("Index", "Home");
}

در صورت موفقیت آمیز بودن عملیات ایجاد حساب کاربری، کاربر به سایت وارد می‌شود.


قانون 1: کلمه‌های عبور باید حداقل 10 کاراکتر باشند

بصورت پیش فرض خاصیت PasswordValidator در کلاس UserManager به کلاس MinimumLengthValidator تنظیم شده است، که اطمینان حاصل می‌کند کلمه عبور حداقل 6 کاراکتر باشد. هنگام وهله سازی UserManager می‌توانید این مقدار را تغییر دهید.
  • مقدار حداقل کاراکترهای کلمه عبور به دو شکل می‌تواند تعریف شود. راه اول، تغییر کنترلر Account است. در متد سازنده این کنترلر کلاس UserManager وهله سازی می‌شود، همینجا می‌توانید این تغییر را اعمال کنید. راه دوم، ساختن کلاس جدیدی است که از UserManager ارث بری می‌کند. سپس می‌توان این کلاس را در سطح global تعریف کرد. در پوشه IdentityExtensions کلاس جدیدی با نام ApplicationUserManager بسازید.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(): base(new UserStore<ApplicationUser>(new ApplicationDbContext()))
    {
        PasswordValidator = new MinimumLengthValidator (10);
    }
}

  کلاس UserManager یک نمونه از کلاس IUserStore را دریافت می‌کند که پیاده سازی API‌های مدیریت کاربران است. از آنجا که کلاس UserStore مبتنی بر Entity Framework است، باید آبجکت DbContext را هم پاس دهیم. این کد در واقع همان کدی است که در متد سازنده کنترلر Account وجود دارد.

یک مزیت دیگر این روش این است که می‌توانیم متدهای UserManager را بازنویسی (overwrite) کنیم. برای پیاده سازی نیازمندهای بعدی دقیقا همین کار را خواهیم کرد.


  • حال باید کلاس ApplicationUserManager را در کنترلر Account استفاده کنیم. متد سازنده و خاصیت UserManager را مانند زیر تغییر دهید.
 public AccountController() : this(new ApplicationUserManager())
         {
         }
  
         public AccountController(ApplicationUserManager userManager)
         {
             UserManager = userManager;
         }
         public ApplicationUserManager UserManager { get; private set; }
حالا داریم از کلاس سفارشی جدیدمان استفاده می‌کنیم. این به ما اجازه می‌دهد مراحل بعدی سفارشی سازی را انجام دهیم، بدون آنکه کدهای موجود در کنترلر از کار بیافتند.
  • اپلیکیشن را اجرا کنید و سعی کنید کاربر محلی جدیدی ثبت نمایید. اگر کلمه عبور وارد شده کمتر از 10 کاراکتر باشد پیغام خطای زیر را دریافت می‌کنید.


قانون 2: کلمه‌های عبور باید حداقل یک عدد و یک کاراکتر ویژه داشته باشند

چیزی که در این مرحله نیاز داریم کلاس جدیدی است که اینترفیس IIdentityValidator را پیاده سازی می‌کند. چیزی که ما می‌خواهیم اعتبارسنجی کنیم، وجود اعداد و کاراکترهای ویژه در کلمه عبور است، همچنین طول مجاز هم بررسی می‌شود. نهایتا این قوانین اعتبارسنجی در متد 'ValidateAsync' بکار گرفته خواهند شد.
  • در پوشه IdentityExtensions کلاس جدیدی بنام CustomPasswordValidator بسازید و اینترفیس مذکور را پیاده سازی کنید. از آنجا که نوع کلمه عبور رشته (string) است از <IIdentityValidator<string استفاده می‌کنیم.
public class CustomPasswordValidator : IIdentityValidator<string> 
{  
    public int RequiredLength { get; set; }

    public CustomPasswordValidator(int length)
    {
        RequiredLength = length;
    }
  
    public Task<IdentityResult> ValidateAsync(string item)
    {
        if (String.IsNullOrEmpty(item) || item.Length < RequiredLength)
        {
            return Task.FromResult(IdentityResult.Failed(String.Format("Password should be of length {0}",RequiredLength)));
        }
  
    string pattern = @"^(?=.*[0-9])(?=.*[!@#$%^&*])[0-9a-zA-Z!@#$%^&*0-9]{10,}$";
   
    if (!Regex.IsMatch(item, pattern))  
    {
        return Task.FromResult(IdentityResult.Failed("Password should have one numeral and one special character"));
    }
  
    return Task.FromResult(IdentityResult.Success);
 }


  در متد ValidateAsync بررس می‌کنیم که طول کلمه عبور معتبر و مجاز است یا خیر. سپس با استفاده از یک RegEx وجود کاراکترهای ویژه و اعداد را بررسی می‌کنیم. دقت کنید که regex استفاده شده تست نشده و تنها بعنوان یک مثال باید در نظر گرفته شود.
  • قدم بعدی تعریف این اعتبارسنج سفارشی در کلاس UserManager است. باید مقدار خاصیت PasswordValidator را به این کلاس تنظیم کنیم. به کلاس ApplicationUserManager که پیشتر ساختید بروید و مقدار خاصیت PasswordValidator را به CustomPasswordValidator تغییر دهید.
public class ApplicationUserManager : UserManager<ApplicationUser>  
{  
    public ApplicationUserManager() : base(new UserStore<ApplicationUser(new ApplicationDbContext()))
    {
        PasswordValidator = new CustomPasswordValidator(10);
    }
 }

  هیچ تغییر دیگری در کلاس AccountController لازم نیست. حال سعی کنید کاربر جدید دیگری بسازید، اما اینبار کلمه عبوری وارد کنید که خطای اعتبارسنجی تولید کند. پیغام خطایی مشابه تصویر زیر باید دریافت کنید.


قانون 3: امکان استفاده از 5 کلمه عبور اخیر ثبت شده وجود ندارد

هنگامی که کاربران سیستم، کلمه عبور خود را بازنشانی (reset) می‌کنند یا تغییر می‌دهند، می‌توانیم بررسی کنیم که آیا مجددا از یک کلمه عبور پیشین استفاده کرده اند یا خیر. این بررسی بصورت پیش فرض انجام نمی‌شود، چرا که سیستم Identity تاریخچه کلمه‌های عبور کاربران را ذخیره نمی‌کند. می‌توانیم در اپلیکیشن خود جدول جدیدی بسازیم و تاریخچه کلمات عبور کاربران را در آن ذخیره کنیم. هربار که کاربر سعی در بازنشانی یا تغییر کلمه عبور خود دارد، مقدار Hash شده را در جدول تاریخچه بررسی میکنیم.
فایل IdentityModels.cs را باز کنید. مانند لیست زیر، کلاس جدیدی بنام 'PreviousPassword' بسازید.
public class PreviousPassword  
{
  
 public PreviousPassword()
 {
    CreateDate = DateTimeOffset.Now;
 }
  
 [Key, Column(Order = 0)]
  
 public string PasswordHash { get; set; }
  
 public DateTimeOffset CreateDate { get; set; }
  
 [Key, Column(Order = 1)]
  
 public string UserId { get; set; }
  
 public virtual ApplicationUser User { get; set; }
  
 }
در این کلاس، فیلد 'Password' مقدار Hash شده کلمه عبور را نگاه میدارد و توسط فیلد 'UserId' رفرنس می‌شود. فیلد 'CreateDate' یک مقدار timestamp ذخیره می‌کند که تاریخ ثبت کلمه عبور را مشخص می‌نماید. توسط این فیلد می‌توانیم تاریخچه کلمات عبور را فیلتر کنیم و مثلا 5 رکورد آخر را بگیریم.

Entity Framework Code First جدول 'PreviousPasswords' را می‌سازد و با استفاده از فیلدهای 'UserId' و 'Password' کلید اصلی (composite primary key) را ایجاد می‌کند. برای اطلاعات بیشتر درباره قرارداهای EF Code First به  این لینک  مراجعه کنید.

  • خاصیت جدیدی به کلاس ApplicationUser اضافه کنید تا لیست آخرین کلمات عبور استفاده شده را نگهداری کند.
public class ApplicationUser : IdentityUser
{
    public ApplicationUser() : base()
    {
        PreviousUserPasswords = new List<PreviousPassword>();
     }

     public virtual IList<PreviousPassword> PreviousUserPasswords { get; set; }
}

 همانطور که پیشتر گفته شد، کلاس UserStore پیاده سازی API‌های لازم برای مدیریت کاربران را در بر می‌گیرد. هنگامی که کاربر برای نخستین بار در سایت ثبت می‌شود باید مقدار Hash کلمه عبورش را در جدول تاریخچه کلمات عبور ذخیره کنیم. از آنجا که UserStore بصورت پیش فرض متدی برای چنین عملیاتی معرفی نمی‌کند، باید یک override تعریف کنیم تا این مراحل را انجام دهیم. پس ابتدا باید کلاس سفارشی جدیدی بسازیم که از UserStore ارث بری کرده و آن را توسعه می‌دهد. سپس از این کلاس سفارشی در ApplicationUserManager بعنوان پیاده سازی پیش فرض UserStore استفاده می‌کنیم. پس کلاس جدیدی در پوشه IdentityExtensions ایجاد کنید.
public class ApplicationUserStore : UserStore<ApplicationUser>
{ 
    public ApplicationUserStore(DbContext context) : base(context)  { }
  
    public override async Task CreateAsync(ApplicationUser user)
    {
        await base.CreateAsync(user);
        await AddToPreviousPasswordsAsync(user, user.PasswordHash);
    }
  
     public Task AddToPreviousPasswordsAsync(ApplicationUser user, string password)
     {
        user.PreviousUserPasswords.Add(new PreviousPassword() { UserId = user.Id, PasswordHash = password });
  
        return UpdateAsync(user);
     }
 }

 متد 'AddToPreviousPasswordsAsync' کلمه عبور را در جدول 'PreviousPasswords' ذخیره می‌کند.

هرگاه کاربر سعی در بازنشانی یا تغییر کلمه عبورش دارد باید این متد را فراخوانی کنیم. API‌های لازم برای این کار در کلاس UserManager تعریف شده اند. باید این متدها را override کنیم و فراخوانی متد مذکور را پیاده کنیم. برای این کار کلاس ApplicationUserManager را باز کنید و متدهای ChangePassword و ResetPassword را بازنویسی کنید.
public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        private const int PASSWORD_HISTORY_LIMIT = 5;

        public ApplicationUserManager() : base(new ApplicationUserStore(new ApplicationDbContext()))
        {
            PasswordValidator = new CustomPasswordValidator(10);
        }

        public override async Task<IdentityResult> ChangePasswordAsync(string userId, string currentPassword, string newPassword)
        {
            if (await IsPreviousPassword(userId, newPassword))
            {
                return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
            }

            var result = await base.ChangePasswordAsync(userId, currentPassword, newPassword);

            if (result.Succeeded)
            {
                var store = Store as ApplicationUserStore;
                await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
            }

            return result;
        }

        public override async Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword)
        {
            if (await IsPreviousPassword(userId, newPassword))
            {
                return await Task.FromResult(IdentityResult.Failed("Cannot reuse old password"));
            }

            var result = await base.ResetPasswordAsync(userId, token, newPassword);

            if (result.Succeeded)
            {
                var store = Store as ApplicationUserStore;
                await store.AddToPreviousPasswordsAsync(await FindByIdAsync(userId), PasswordHasher.HashPassword(newPassword));
            }

            return result;
        }

        private async Task<bool> IsPreviousPassword(string userId, string newPassword)
        {
            var user = await FindByIdAsync(userId);

            if (user.PreviousUserPasswords.OrderByDescending(x => x.CreateDate).
                Select(x => x.PasswordHash).Take(PASSWORD_HISTORY_LIMIT)
                .Where(x => PasswordHasher.VerifyHashedPassword(x, newPassword) != PasswordVerificationResult.Failed).Any())
            {
                return true;
            }

            return false;
        }
    }
فیلد 'PASSWORD_HISTORY_LIMIT' برای دریافت X رکورد از جدول تاریخچه کلمه عبور استفاده می‌شود. همانطور که می‌بینید از متد سازنده کلاس ApplicationUserStore برای گرفتن متد جدیدمان استفاده کرده ایم. هرگاه کاربری سعی می‌کند کلمه عبورش را بازنشانی کند یا تغییر دهد، کلمه عبورش را با 5 کلمه عبور قبلی استفاده شده مقایسه می‌کنیم و بر این اساس مقدار true/false بر می‌گردانیم.

کاربر جدیدی بسازید و به صفحه Manage  بروید. حال سعی کنید کلمه عبور را تغییر دهید و از کلمه عبور فعلی برای مقدار جدید استفاده کنید تا خطای اعتبارسنجی تولید شود. پیغامی مانند تصویر زیر باید دریافت کنید.

سورس کد این مثال را می‌توانید از این لینک دریافت کنید. نام پروژه Identity-PasswordPolicy است، و زیر قسمت Samples/Identity قرار دارد.

مطالب
فیلدهای پویا در NHibernate

یکی از قابلیت‌های جالب NHibernate امکان تعریف فیلدها به صورت پویا هستند. به این معنا که زیرساخت طراحی یک برنامه "فرم ساز" هم اکنون در اختیار شما است! سیستمی که امکان افزودن فیلدهای سفارشی را دارا است که توسط برنامه نویس در زمان طراحی اولیه آن ایجاد نشده‌اند. در ادامه نحوه‌ی تعریف و استفاده از این قابلیت را توسط Fluent NHibernate بررسی خواهیم کرد.

در اینجا کلاسی که قرار است توانایی افزودن فیلدهای سفارشی را داشته باشد به صورت زیر تعریف می‌شود:
using System.Collections;

namespace TestModel
{
public class DynamicEntity
{
public virtual int Id { get; set; }
public virtual IDictionary Attributes { set; get; }
}
}

Attributes در عمل همان فیلدهای سفارشی مورد نظر خواهند بود. جهت معرفی صحیح این قابلیت نیاز است تا نگاشت آن‌را از نوع dynamic component تعریف کنیم:
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace TestModel
{
public class DynamicEntityMapping : IAutoMappingOverride<DynamicEntity>
{
public void Override(AutoMapping<DynamicEntity> mapping)
{
mapping.Table("tblDynamicEntity");
mapping.Id(x => x.Id);
mapping.IgnoreProperty(x => x.Attributes);
mapping.DynamicComponent(x => x.Attributes,
c =>
{
c.Map(x => (string)x["field1"]);
c.Map(x => (string)x["field2"]).Length(300);
c.Map(x => (int)x["field3"]);
c.Map(x => (double)x["field4"]);
});
}
}
}
و مهم‌ترین نکته‌ی این بحث هم همین نگاشت فوق است.
ابتدا از IgnoreProperty جهت ندید گرفتن Attributes استفاده کردیم. زیرا درغیراینصورت در حالت Auto mapping ، یک رابطه چند به یک به علت وجود IDictionary به صورت خودکار ایجاد خواهد شد که نیازی به آن نیست (یافتن این نکته نصف روز کار برد ....! چون مرتبا خطای An association from the table DynamicEntity refers to an unmapped class: System.Collections.Idictionary ظاهر می‌شد و مشخص نبود که مشکل از کجاست).
سپس Attributes به عنوان یک DynamicComponent معرفی شده است. در اینجا چهار فیلد سفارشی را اضافه کرده‌ایم. به این معنا که اگر نیاز باشد تا فیلد سفارشی دیگری به سیستم اضافه شود باید یکبار Session factory ساخته شود و SchemaUpdate فراخوانی گردد و خوشبختانه با وجود Fluent NHibernate ، تمام این تغییرات در کدهای برنامه قابل انجام است (بدون نیاز به سر و کار داشتن با فایل‌های XML نگاشت‌ها و ویرایش دستی آن‌ها). از تغییر نام جدول که برای مثال در اینجا tblDynamicEntity در نظر گرفته شده تا افزودن فیلدهای دیگر در قسمت DynamicComponent فوق.
همچنین باتوجه به اینکه این نوع تغییرات (ساخت دوبار سشن فکتوری) مواردی نیستند که قرار باشد هر ساعت انجام شوند، بنابراین سربار آنچنانی را به سیستم تحمیل نمی‌کنند.
   drop table tblDynamicEntity

create table tblDynamicEntity (
Id INT IDENTITY NOT NULL,
field1 NVARCHAR(255) null,
field2 NVARCHAR(300) null,
field3 INT null,
field4 FLOAT null,
primary key (Id)
)

اگر با SchemaExport، اسکریپت خروجی معادل با نگاشت فوق را تهیه کنیم به جدول فوق خواهیم رسید. نوع و طول این فیلدهای سفارشی بر اساس نوعی که برای اشیاء دیکشنری مشخص می‌کنید، تعیین خواهند شد.

چند مثال جهت کار با این فیلدهای سفارشی یا پویا :

نحوه‌ی افزودن رکوردهای جدید بر اساس خاصیت‌های سفارشی:
//insert
object savedId = 0;
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var obj = new DynamicEntity();
obj.Attributes = new Hashtable();
obj.Attributes["field1"] = "test1";
obj.Attributes["field2"] = "test2";
obj.Attributes["field3"] = 1;
obj.Attributes["field4"] = 1.1;

savedId = session.Save(obj);
tx.Commit();
}
}

با خروجی
INSERT
INTO
tblDynamicEntity
(field1, field2, field3, field4)
VALUES
(@p0, @p1, @p2, @p3);
@p0 = 'test1' [Type: String (0)], @p1 = 'test2' [Type: String (0)], @p2 = 1
[Type: Int32 (0)], @p3 = 1.1 [Type: Double (0)]

نحوه‌ی کوئری گرفتن از این اطلاعات (فعلا پایدارترین روشی را که برای آن یافته‌ام استفاده از HQL می‌باشد ...):
//query
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
//using HQL
var list = session
.CreateQuery("from DynamicEntity d where d.Attributes.field1=:p0")
.SetString("p0", "test1")
.List<DynamicEntity>();

if (list != null && list.Any())
{
Console.WriteLine(list[0].Attributes["field2"]);
}
tx.Commit();
}
}
با خروجی:
    select
dynamicent0_.Id as Id1_,
dynamicent0_.field1 as field2_1_,
dynamicent0_.field2 as field3_1_,
dynamicent0_.field3 as field4_1_,
dynamicent0_.field4 as field5_1_
from
tblDynamicEntity dynamicent0_
where
dynamicent0_.field1=@p0;
@p0 = 'test1' [Type: String (0)]

استفاده از HQL هم یک مزیت مهم دارد: چون به صورت رشته قابل تعریف است، به سادگی می‌توان آن‌را داخل دیتابیس ذخیره کرد. برای مثال یک سیستم گزارش ساز پویا هم در این کنار طراحی کرد ....

نحوه‌ی به روز رسانی و حذف اطلاعات بر اساس فیلدهای پویا:
//update
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var entity = session.Get<DynamicEntity>(savedId);
if (entity != null)
{
entity.Attributes["field2"] = "new-val";
tx.Commit(); // Persist modification
}
}
}

//delete
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var entity = session.Get<DynamicEntity>(savedId);
if (entity != null)
{
session.Delete(entity);
tx.Commit();
}
}
}

نظرات مطالب
راه اندازی StimulSoft Report در ASP.NET MVC
مطلب «نحوه صحیح تولید Url در ASP.NET MVC» را پیگیری کنید (چون هر کدام از نام‌های اکشن متدهای درج شده‌ی در View در حقیقت یک URL هستند و نه صرفا یک نام خالی و اگر نیاز به آدرس صحیح آن‌ها را دارید باید از سیستم مسیریابی، آن‌ها را توسط Url.Action کوئری بگیرید).  
نظرات مطالب
طریقه بررسی صحت کدملی به کمک متدهای الحاقی
یک حالت جدید پیدا کردم که مطمئنا خود ثبت احوال هم نمی‌دونه! هر عدد ده رقمی که به فرم قربنه وارد بشه، صحیح فرض میشه!
مثلا a00000000a  یا 1234554321 یا 1346886431 همگی صحیح فرض میشن حال آنکه اشتباه هستن! 
نظرات مطالب
چند نکته کاربردی درباره Entity Framework
اگر این توضیح صحیح باشه باید برای هر تعداد از عتاصر هم صحیح باشه. یعنی با هر تعداد شیی در لیست. خوب حالا فرض کنید لیست ما 1 عنصر داره. این یعنی همون حالت "کار با یک شیی ساده".  چرا در این حالت توضیحی که شما گفتید صادق نیست؟ 
مطالب
پیاده سازی authorization به روش AOP به کمک کتابخانه های SNAP و StructureMap
همانطور که پیشتر در این مقاله بحث شده است، بوسیله AOP می‌توان قابلیت‌هایی که قسمت عمده‌ای از برنامه را تحت پوشش قرار می‌دهند، کپسوله کرد. یکی از قابلیت‌هایی که در بخشهای مختلف یک سیستم نرم‌افزاری مورد نیاز است، Authorization یا اعتبارسنجی‌ست. در ادامه به بررسی یک پیاده‌سازی به این روش می‌پردازیم.
 
کتابخانه SNAP
کتابخانه SNAP به گفته سازنده آن، با یکپارچه‌سازی AOP با IoC Container‌های محبوب، برنامه‌نویسی به این سبک را ساده می‌کند. این کتابخانه هم اکنون علاوه بر structureMap از IoC Providerهای Autofac, Ninject, LinFu و Castle Windsor نیز پشتیبانی میکند. 
دریافت SNAP.StructureMap 
برای دریافت آن نیاز است دستور پاورشل ذیل را در کنسول نیوگت ویژوال استودیو اجرا کنید:
PM> Install-Package snap.structuremap
پس از اجرای دستور فوق، کتابخانه SNAP.StructureMap که در زمان نگارش این مطلب نسخه 1.8.0 آن موجود است به همراه کلیه نیازمندی‌های آن که شامل موارد زیر می‌باشد نصب خواهد شد.
StructureMap (≥ 2.6.4.1)
CommonServiceLocator.StructureMapAdapter (≥ 1.1.0.3)
SNAP (≥ 1.8)
fasterflect (≥ 2.1.2)
Castle.Core (≥ 3.1.0)
CommonServiceLocator (≥ 1.0)

تنظیمات SNAP 

از آنجا که تنظیمات SNAP همانند تنظیمات StructureMap تنها باید یک بار اجرا شود، بهترین جا برای آن در یک برنامه وب، Application_Start فایل Global.asax است.
namespace Framework.UI.Asp
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            initSnap();
            initStructureMap();
        }

        private static void initSnap()
        {
            SnapConfiguration.For<StructureMapAspectContainer>(c =>
            {
                // Tell Snap to intercept types under the "Framework.ServiceLayer..." namespace.
                c.IncludeNamespace("Framework.ServiceLayer.*");
                // Register a custom interceptor (a.k.a. an aspect).
                c.Bind<Framework.ServiceLayer.Aspects.AuthorizationInterceptor>()
                .To<Framework.ServiceLayer.Aspects.AuthorizationAttribute>();
            });
        }

        void Application_EndRequest(object sender, EventArgs e)
        {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        }

        private static void initStructureMap()
        {
            var thread = StructureMap.Pipeline.Lifecycles.GetLifecycle(InstanceScope.HttpSession);
            ObjectFactory.Configure(x =>
            {
                x.For<IUserManager>().Use<EFUserManager>();
                x.For<IAuthorizationManager>().LifecycleIs(thread)
                 .Use<EFAuthorizationManager>().Named("_AuthorizationManager");                
                x.For<Framework.DataLayer.IUnitOfWork>()
                 .Use<Framework.DataLayer.Context>();

                x.SetAllProperties(y =>
                {
                    y.OfType<IUserManager>();
                    y.OfType<Framework.DataLayer.IUnitOfWork>();
                    y.OfType<Framework.Common.Web.IPageHelpers>();
                });
            });
        }
    }
}


بخش اعظم کدهای فوق در مقاله‌های «استفاده از StructureMap به عنوان یک IoC Container» و «تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET Web forms» شرح داده شده‌اند، تنها بخش جدید متد ()initSnap است، که خط اول آن به snap می‌گوید همه کلاس‌هایی که در فضای نام Framework.ServiceLayer و زیرمجموعه‌های آن هستند را پوشش دهد. خط دوم نیز کلاس AuthorizationInterceptor را به عنوان مرجعی برای handle کردن AuthorizationAttribute معرفی می‌کند.
در ادامه به بررسی کلاس AuthorizationInterceptor می‌پردازیم.
namespace Framework.ServiceLayer.Aspects
{
    public class AuthorizationInterceptor : MethodInterceptor
    {
        public override void InterceptMethod(IInvocation invocation, MethodBase method, Attribute attribute)
        {
            var AuthManager = StructureMap.ObjectFactory
.GetInstance<Framework.ServiceLayer.UserManager.IAuthorizationManager>();
            var FullName = GetMethodFullName(method);
            if (!AuthManager.IsActionAuthorized(FullName))
                throw new Common.Exceptions.UnauthorizedAccessException("");

            invocation.Proceed(); // the underlying method call
        }

        private static string GetMethodFullName(MethodBase method)
        {
            var TypeName = (((System.Reflection.MemberInfo)(method)).DeclaringType).FullName;
            return TypeName + "." + method.Name;
        }
    }

    public class AuthorizationAttribute : MethodInterceptAttribute
    { }
کلاس مذکور از کلاس MethodInterceptor کتابخانه snap ارث بری کرده و متد InterceptMethod را تحریف میکند. این متد، کار اجرای متد اصلی ای که با این Aspect تزئین شده را بر عهده دارد. بنابراین می‌توان پیش از اجرای متد اصلی،  اعتبارسنجی را انجام داد.
 
کلاس MethodInterceptor   
کلاس MethodInterceptor  شامل چندین متد دیگر نیز هست که میتوان برای سایر مقاصد از جمله مدیریت خطا و Event logging از آنها استفاده کرد.
namespace Snap
{
    public abstract class MethodInterceptor : IAttributeInterceptor, IInterceptor, IHideBaseTypes
    {
        protected MethodInterceptor();

        public int Order { get; set; }
        public Type TargetAttribute { get; set; }

        public virtual void AfterInvocation();
        public virtual void BeforeInvocation();
        public void Intercept(IInvocation invocation);
        public abstract void InterceptMethod(IInvocation invocation, MethodBase method, Attribute attribute);
        public bool ShouldIntercept(IInvocation invocation);
    }
}
 

یک نکته  

نکته مهمی که در اینجا پیش می‌آید این است که برای اعتبارسنجی، کد کاربری شخصی که لاگین کرده، باید به طریقی در اختیار متد ()IsActionAuthorized قرار بگیرد. برای این کار می‌توان در یک HttpMudole به عنوان مثال همان ماژولی که برای تسهیل در کار تزریق خودکار وابستگی‌ها در سطح فرم‌ها استفاده می‌شود، با استفاده از امکانات structureMap به وهله‌ی ایجاد شده از AuthorizationManager (که با کمک structureMap با طول عمر InstanceScope.HttpSession ساخته شده است) دسترسی پیدا کرده و خاصیت مربوطه را مقداردهی کرد.
        private void Application_PreRequestHandlerExecute(object source, EventArgs e)
        {
            var page = HttpContext.Current.Handler as BasePage; // The Page handler
            if (page == null)
                return;

            WireUpThePage(page);
            WireUpAllUserControls(page);

            var UsrCod = HttpContext.Current.Session["UsrCod"];
            if (UsrCod != null)
            {
                var _AuthorizationManager = ObjectFactory
                    .GetNamedInstance<Framework.ServiceLayer.UserManager.IAuthorizationManager>("_AuthorizationManager");

                ((Framework.ServiceLayer.UserManager.EFAuthorizationManager)_AuthorizationManager)
                    .AuditUserId = UsrCod.ToString();
            }
        }
 
روش استفاده
نحوه استفاده از Aspect تعریف شده در کد زیر قابل مشاهده است:
namespace Framework.ServiceLayer.UserManager
{
    public class EFUserManager : IUserManager
    {
        IUnitOfWork _uow;
        IDbSet<User> _users;

        public EFUserManager(IUnitOfWork uow)
        {
            _uow = uow;
            _users = _uow.Set<User>();
        }

        [Framework.ServiceLayer.Aspects.Authorization]
        public List<User> GetAll()
        {
            return _users.ToList<User>();
        }
    }
}

مطالب
OpenCVSharp #7
معرفی اینترفیس ++C کتابخانه‌ی OpenCVSharp

اینترفیس یا API زبان C کتابخانه‌ی OpenCV مربوط است به نگارش‌های 1x این کتابخانه و تمام مثال‌هایی را که تاکنون ملاحظه کردید، بر مبنای همین اینترفیس تهیه شده بودند. اما از OpenCV سری 2x، این اینترفیس صرفا جهت سازگاری با نگارش‌های قبلی، نگهداری می‌شود و اینترفیس اصلی مورد استفاده، API جدید ++C آن است. به همین جهت کتابخانه‌ی OpenCVSharp نیز در فضای نام OpenCvSharp.CPlusPlus و توسط اسمبلی OpenCvSharp.CPlusPlus.dll، امکان دسترسی به این API جدید را فراهم کرده‌است که در ادامه نکات مهم آن‌را بررسی خواهیم کرد.


تبدیل مثال‌های اینترفیس C به اینترفیس ++C

مثال «تبدیل تصویر به حالت سیاه و سفید» قسمت سوم را درنظر بگیرید. این مثال به کمک اینترفیس C کتابخانه‌ی OpenCV کار می‌کند. معادل تبدیل شده‌ی آن به اینترفیس ++C به صورت ذیل است:
// Cv2.ImRead
using (var src = new Mat(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor))
using (var dst = new Mat())
{
    Cv2.CvtColor(src, dst, ColorConversion.BgrToGray);
 
    // How to export
    using (var bitmap = dst.ToBitmap()) // => OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dst)
    {
        bitmap.Save("gray.png", ImageFormat.Png);
    }
 
    using (new Window("BgrToGray C++: src", image: src))
    using (new Window("BgrToGray C++: dst", image: dst))
    {
        Cv2.WaitKey();
    }
}
نکاتی را که باید در اینجا مدنظر داشت:
- بجای IplImage، از کلاس Mat استفاده شده‌است.
- برای ایجاد Clone یک تصویر نیازی نیست تا پارامترهای خاصی را به Mat دوم (همان dst) انتساب داد و ایجاد یک Mat خالی کفایت می‌کند.
- اینبار بجای کلاس Cv اینترفیس C، از کلاس Cv2 اینترفیس ++C استفاده شده‌است.
- متد الحاقی ToBitmap نیز که در کلاس OpenCvSharp.Extensions.BitmapConverter قرار دارد، با نمونه‌ی Mat سازگار است و به این ترتیب می‌توان خروجی معادل دات نتی Mat را با فرمت Bitmap تهیه کرد.
- بجای CvWindow، در اینجا باید از Window سازگار با Mat، استفاده شود.
- new Mat معادل Cv2.ImRead است. بنابراین اگر مثال ++C ایی را در اینترنت یافتید:
cv::Mat src = cv::imread ("foo.jpg");
cv::Mat dst;
cv::cvtColor (src, dst, CV_BGR2GRAY);
معادل متد imread آن همان new Mat کتابخانه‌ی OpenCVSharp است و یا متد Cv2.ImRead آن.


کار مستقیم با نقاط در OpenCVSharp

متدهای ماتریسی OpenCV، فوق العاده در جهت سریع اجرا شدن و استفاده‌ی از امکانات سخت افزاری و پردازش‌های موازی، بهینه سازی شده‌اند. اما اگر قصد داشتید این متدهای سریع را با نمونه‌هایی متداول و نه چندان سریع جایگزین کنید، می‌توان مستقیما با نقاط تصویر نیز کار کرد. در ادامه قصد داریم کار فیلتر توکار Not را که عملیات معکوس سازی رنگ نقاط را انجام می‌دهد، شبیه سازی کنیم.

در اینجا نحوه‌ی دسترسی مستقیم به نقاط تصویر بارگذاری شده را توسط اینترفیس C، ملاحظه می‌کنید:
using (var src = new IplImage(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor))
using (var dst = new IplImage(src.Size, src.Depth, src.NChannels))
{
    for (var y = 0; y < src.Height; y++)
    {
        for (var x = 0; x < src.Width; x++)
        {
            CvColor pixel = src[y, x];
            dst[y, x] = new CvColor
            {
                B = (byte)(255 - pixel.B),
                G = (byte)(255 - pixel.G),
                R = (byte)(255 - pixel.R)
            };
        }
    }
 
    // [C] Accessing Pixel
    // https://github.com/shimat/opencvsharp/wiki/%5BC%5D-Accessing-Pixel
 
    using (new CvWindow("C Interface: Src", image: src))
    using (new CvWindow("C Interface: Dst", image: dst))
    {
        Cv.WaitKey(0);
    }
}
IplImage امکان دسترسی به نقاط را به صورت یک آرایه‌ی دو بعدی میسر می‌کند. خروجی آن از نوع CvColor است که در اینجا از هر عنصر آن، 255 واحد کسر خواهد شد تا فیلتر Not شبیه سازی شود. سپس این رنگ جدید، به نقطه‌ای معادل آن در تصویر خروجی انتساب داده می‌شود.
روش ارائه شده‌ی در اینجا یکی از روش‌های دسترسی به نقاط، توسط اینترفیس C است. سایر روش‌های ممکن را در Wiki آن می‌توانید مطالعه کنید.


شبیه به همین کار را می‌توان به نحو ذیل توسط اینترفیس ++C کتابخانه‌ی OpenCVSharp نیز انجام داد:
// Cv2.ImRead
using (var src = new Mat(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor))
using (var dst = new Mat())
{
    src.CopyTo(dst);
 
    for (var y = 0; y < src.Height; y++)
    {
        for (var x = 0; x < src.Width; x++)
        {
            var pixel = src.Get<Vec3b>(y, x);
            var newPixel = new Vec3b
            {
                Item0 = (byte)(255 - pixel.Item0), // B
                Item1 = (byte)(255 - pixel.Item1), // G
                Item2 = (byte)(255 - pixel.Item2) // R
            };
            dst.Set(y, x, newPixel);
        }
    }
 
    // [Cpp] Accessing Pixel
    // https://github.com/shimat/opencvsharp/wiki/%5BCpp%5D-Accessing-Pixel
 
    //Cv2.NamedWindow();
    //Cv2.ImShow();
    using (new Window("C++ Interface: Src", image: src))
    using (new Window("C++ Interface: Dst", image: dst))
    {
        Cv2.WaitKey(0);
    }
}
ابتدا توسط کلاس Mat، کار بارگذاری و سپس تهیه‌ی یک کپی، از تصویر اصلی انجام می‌شود. در ادامه برای دسترسی به نقاط تصویر، از متد Get که خروجی آن از نوع Vec3b است، استفاده خواهد شد. این بردار دارای سه جزء است که بیانگر اجزای رنگ نقطه‌ی مدنظر می‌باشند. در اینجا نیز 255 واحد از هر جزء کسر شده و سپس توسط متد Set، به تصویر خروجی اعمال خواهند شد.
می‌توانید سایر روش‌های دسترسی به نقاط را توسط اینترفیس ++C، در Wiki این کتابخانه مطالعه نمائید.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
مطالب
OpenCVSharp #17
تشخیص اشخاص به کمک OpenCV

فرض کنید قصد دارید یک سیستم حضور غیاب مبتنی بر تشخیص چهره را طراحی کنید. قسمت استخراج چهره، از تصویر کلی رسیده را بررسی کردیم. اما در ادامه چگونه تشخیص دهیم که این چهره متعلق به چه شخصی است؟ با توجه به اینکه تصویر چهره‌ی یک شخص می‌تواند از زوایای مختلفی تهیه شود و یا حتی حالات روحی منعکس شده‌ی در صورت نیز در تغییر بیت و بایت‌های تصویر چهره مؤثر هستند.


بانک اطلاعاتی تصاویر چهره‌های اشخاص

در اینجا از تصاویر «The Database of Faces» استفاده خواهیم کرد. این مجموعه شامل تصاویر 40 شخص، در 10 حالت مختلف است.


برای بارگذاری این تصاویر و استفاده‌ی از آن‌ها در الگوریتم FisherFaceRecognizer نیاز به ساختار ذیل است:
public class ImageInfo
{
    public Mat Image { set; get; }
    public int ImageGroupId { set; get; }
    public int ImageId { set; get; }
}
در اینجا Image، محتوای تصویر انتخابی است. مقدار ImageGroupId مساوی مقدار عددی نام پوشه‌ی تصاویر منهای یک، تنظیم می‌شود. برای مثال پوشه‌ی s1 به گروه صفر تنظیم می‌شود. ImageId نیز به یک مقدار خود افزایش یابنده معادل شماره‌ی جاری تصویر، تنظیم می‌گردد؛ به این صورت:
var images = new List<ImageInfo>();
 
var imageId = 0;
foreach (var dir in new DirectoryInfo(@"..\..\Images").GetDirectories())
{
    var groupId = int.Parse(dir.Name.Replace("s", string.Empty)) - 1;
    foreach (var imageFile in dir.GetFiles("*.pgm"))
    {
        images.Add(new ImageInfo
        {
            Image = new Mat(imageFile.FullName, LoadMode.GrayScale),
            ImageId = imageId++,
            ImageGroupId = groupId
        });
    }
}
ابتدا پوشه‌های دیتابیس تصاویر یافت شده و سپس از نام هر پوشه یک شما‌‌ره‌ی گروه (یا شماره‌ی شخص) استخراج می‌شود. سپس تصاویر این پوشه به لیست تصاویر اصلی اضافه خواهند شد.


تشخیص یک چهره‌ی اتفاقی

پس از تشکیل لیست تصاویر، اکنون کار با الگوریتم FisherFaceRecognizer به نحو ذیل خواهد بود:
var model = FaceRecognizer.CreateFisherFaceRecognizer();
model.Train(images.Select(x => x.Image), images.Select(x => x.ImageGroupId));
 
var rnd = new Random();
var randomImageId = rnd.Next(0, images.Count - 1);
var testSample = images[randomImageId];
 
Console.WriteLine("Actual group: {0}", testSample.ImageGroupId);
Cv2.ImShow("actual", testSample.Image);
 
var predictedGroupId = model.Predict(testSample.Image);
Console.WriteLine("Predicted group: {0}", predictedGroupId);
پارامتر اول متد Train، لیست تصاویر است و پارامتر دوم، لیست شماره گروه‌های متناظر با هر تصویر است که در اینجا به عنوان برچسب نیز نامگذاری شده‌است.
سپس با استفاده از کلاس Random، یک تصویر اتفاقی انتخاب می‌شود.
اکنون این تصویر اتفاقی به متد Predict ارسال شده و نتیجه‌ی آن، شماره گروه چهره‌ی تشخیص داده شده‌است. به این ترتیب می‌توان تشخیص داد که یک تصویر مفروض ورودی، متعلق به چه شخصی (یا در اینجا گروه یا برچسب) است.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
مطالب
معرفی DNTProfiler
پیشاپیش فرا رسیدن سال نو را به تمام همراهان گرامی سایت net tips. تبریک عرض می‌کنم. به امید سالی پر از سلامتی و رونق، به همراه اشتیاق روز افزون جستجوگری و کشف زوایای پنهان دنیای برنامه نویسی!
هد‌یه‌ی نوروزی سایت net tips. پروژه‌ی پروفایلر سورس بازی است که با EF 6.x و همچنین NHibernate 4.x سازگار است. این پروژه از دو قسمت کلاینت و سرور تشکیل می‌شود.


نصب کلاینت EF برنامه‌ی DNTProfiler

تفاوتی نمی‌کند که برنامه‌ی شما وبی است یا ویندوزی؛ برای هر دو حالت ابتدا دستور ذیل را در کنسول پاورشل نیوگت اجرا کنید:
 PM> Install-Package DNTProfiler.EntityFramework.Core
با اینکار interceptor‌های پروفایلر نصب می‌شوند. برای فعال سازی آن نیاز است چند سطر ذیل را به فایل app.config/web.config پروژه‌ی خود اضافه کنید:
<configuration>
  <entityFramework>
    <interceptors>
      <interceptor type="DNTProfiler.EntityFramework.Core.DatabaseLogger, DNTProfiler.EntityFramework.Core">
        <parameters>
          <parameter value="http://localhost:8080" />
          <parameter value="|DataDirectory|\ErrorsLog.Log" />
        </parameters>
      </interceptor>
    </interceptors>
  </entityFramework>
</configuration>


دریافت و راه اندازی برنامه‌ی DNTProfiler

آخرین نگارش برنامه‌ی DNTProfiler را از برگه‌ی releases مخزن کد آن می‌توانید دریافت کنید:
https://github.com/VahidN/DNTProfiler/releases

این برنامه برای دات نت 4 نوشته شده‌است. بنابراین اگر هنوز از ویندوز XP استفاده می‌کنید، امکان کار کردن با آن‌را خواهید داشت.
البته بسته‌ی نیوگت DNTProfiler.EntityFramework.Core آن برای دات نت 4 و 4.5 تهیه شده‌است و به صورت خودکار بر اساس ساختار پروژه‌ی شما، یکی از آن‌ها نصب خواهد شد.

تا اینجا کار راه اندازی این برنامه به پایان می‌رسد. برای استفاده‌ی از آن باید ابتدا برنامه‌ی DNTProfiler را اجرا کنید. این برنامه به پیام‌های رسیده‌ی از برنامه‌ی اصلی شما (ارسال شده توسط DNTProfiler.EntityFramework.Core) گوش فرا می‌دهد و سپس شروع به آنالیز آن‌ها خواهد کرد. ساختار این تبادل اطلاعات هم بر اساس تهیه‌ی یک ASP.NET Self host Web API است.


این برنامه به صورت ماژولار تهیه شده‌است و تمام آنالیز کننده‌های آن در حقیقت یک پلاگین هستند (در حال حاضر دارای 32 پلاگین است). این پلاگین‌ها در گروه‌های Alerts (برای مثال یافتن جوین‌های تکراری و یک سری موارد بهینه سازی سرعت)، Loggers (طبقه بندی خام اطلاعات رسیده)، Visualizers (نمایش بصری اطلاعات رسیده) قرار می‌گیرند.


نظرات، پیشنهادات و همکاری


لطفا برای طرح سؤالات و ارائه‌ی پیشنهادات خود در زمینه‌ی این پروژه، به قسمت اختصاصی آن در سایت مراجعه نمائید:
https://www.dntips.ir/projects/details/21