مطالب
نمایش خودکار آیکون لینک‌های سایت‌های خارجی با استفاده از jQuery

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

<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js' type='text/javascript' />
<script type="text/javascript">
$(document).ready(function(){
$("a").each(function(){
var $a = $(this);
var href = $a.attr("href");
// see if the link is external
if (href && href.match(/^http/))
if (!href.match(document.domain)) {
var domain = href.replace(/<\S[^><]*>/g, "").split('/')[2];
var image = '<img src="http://' + domain +
'/favicon.ico" width="0" ' +
' onload="this.width=16;this.height=16;this.style.paddingLeft=\'3px\';this.style.paddingRight=\'1px\';" ' +
' style="border:0" ' +
' onerror="this.src=\'http://vahid.nasiri.googlepages.com/weblink.gif\';" />';
$(this).prepend(image);
}
});
});
</script>

توضیحاتی در مورد جزئیات برنامه نویسی آن:
با استفاده از jQuery ، زمانیکه document ما ready شد ( document.ready )، تمام لینک‌های موجود در صفحه را پیدا می‌کنیم (یک به یک). سپس مقدار href آن‌ها را می‌خوانیم (ببینید با jQuery انجام این نوع کارها چقدر ساده شده است). اکنون روی لینک دریافت شده باید پردازش صورت گیرد و نام دومین آن جدا شود (اگر ختم به خارج از سایت بود). سپس بر اساس این دومین ، یک تگ img منتهی به آیکون آن سایت طراحی و نمایش داده خواهد شد (به قبل از لینک اضافه می‌شود).
عموما هر پروژه‌ای هر چند کوچک و به ظاهر کم اهمیت، نکات خاصی را به همراه دارد.
برای مثال، در ابتدای کار width=0 در نظر گرفته نشد. مشکلی را که ایجاد کرد این بود که یک سایت ممکن است اصلا favicon.ico نداشته باشد. فایرفاکس محترم اصلا به روی مبارک هم نخواهد آورد و شما متوجه نخواهید شد که در صفحه کمبود تصویری وجود دارد. اما در IE حتما جای خالی آیکون و تصویر به نحوه واضحی گوشزد می‌شود. بنابراین در ابتدای کار قرار نیست چیزی را نمایش دهیم (width=0). سپس در رخ‌داد onload تگ img، اگر تصویر واقعا وجود داشت و بارگذاری شد، طول و عرض آن‌را 16 در 16 قرار خواهیم داد (این مورد هم لازم است. چون بعضی از سایت‌ها اندازه‌های بسیار بزرگی را برای آیکون خود در نظر می‌گیرند که اصلا مقصود ما را برآورده نخواهند کرد).
این مورد (عدم نمایش تصاویری که وجود ندارند) مشکلی را که در ادامه ایجاد خواهد کرد، عدم یکنواختی در سایت است. یک سری از لینک‌های خارجی آیکون دارند و یک سری خیر. نکته جالبی که در مورد تگ img وجود دارد رخ‌داد onerror آن است. اگر مرورگر نتواند تصویر مورد نظر را پیدا یا بارگذاری کند، این روال رخ‌داد‌ گردان فراخوانی می‌شود. همینجا از موقعیت استفاده شده و src تصویر جاری به یک آیکون مشخص و از قبل تعیین شده تنظیم می‌شود.

مطالب
معرفی سرویس‌های ارائه شده توسط شرکت‌های گوگل، آمازون و مایکروسافت در قالب رایانش ابری - قسمت اول

رایانش ابری مفهوم نسبتا جدیدی در عرصه‌ی فناوری اطلاعات است و در حال گسترش می­‌باشد. به طور خلاصه رایانش ابری به همه چیز اعم از برنامه کاربردی( Application )، سکو ی ( Platform ) توسعه نرم افزار، سخت افزار و زیرساخت، به عنوان سرویس نگاه می‌کند. زیرساخت های موجود در مراکز داده( Data Center ) به انضمام  نرم‌افزارهایی که در آن قرار دارند، مجموعه‌ه­ایی را تشکیل می­‌دهند که ابر نامیده می­‌شود. به عبارت ساده­‌تر رایانش ابری یعنی استفاده­ اشتراکی از برنامه­‌ها و منابع یک محیط شبکه­‌ای برای انجام یک کار، بدون این که مالکیت، مدیریت منابع شبکه و سخت­ افزار و برنامه­‌ها، برای استفاده کننده مهم باشد. در رایانش ابری منابع کامپیوترها، برای انجام یک کار استفاده می‌شوند و داده­‌های مربوط به پردازش، در هیچ کدام از کامپیوترهای شخصی ذخیره نمی­‌شوند، بلکه در جای دیگری در داخل همان منابع شبکه، ذخیره می­‌شوند تا در زمان و مکان دیگری قابل دسترسی باشند.

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

 سرویس­‌های رایانش ابری گوگل، در زیر دو چتر قرار دارند. گروه اول شامل مجموعه گسترده­ای از برنامه­‌های محبوب گوگل مانند Google Doc ،   Google Health ، Google Mail ، Google Earth هستند که با کلیک بر روی گزینه More و Even More که در بالای صفحه اصلی گوگل قرار دارند، می­توان به آن­ها دسترسی پیدا کرد.

دومین محصول مبتنی بر ابر گوگل، ابزار توسعه PaaS گوگل است. این سکو در سال 2008 برای توسعه برنامه‌­های تحت وب، با استفاده از زیرساخت گوگل به نام موتور Google App  معرفی شد. هدف از آن قادر ساختن توسعه دهندگان برای ساخت و استقرار برنامه­‌های وب بدون نگرانی از زیرساختی است که برنامه بر رویش اجرا می­شود. برنامه­‌های این موتور، با زبان­های سطح بالا به ویژه جاوا و پایتون و در چارچوب GAE نوشته می­‌شوند. گوگل به منظور گسترش این نوع برنامه­‌ها یک سطح رایگان مشخص از سرویس را ارائه می­دهد و زمانی که برنامه از سطح مشخصی از بار پردازشی، ذخیره­‌سازی و پهنای باند شبکه فراتر رفت، آنگاه شارژها بر مبنای میزان استفاده محاسبه می­شود.

برنامه­‌های GAE را باید به گونه­‌ای نوشت که با زیرساخت گوگل وفق یابند. این مسئله، باعث محدودیت برنامه­‌های قابل اجرا در GAE  می­گردد و علاوه بر آن، انتقال برنامه‌­ها به سکوی GAE و یا انتقال از این سکو به سایر سکو­های موجود دشوار می­شود.

از میان سرویس های ابری رایگان ارائه شده از سوی گوگل، به معرفی سرویس آنالیز گوگل بسنده کرده و تمرکز اصلی بر روی سکوی توسعه نرم‌افزاری این شرکت ( GAE ) می‌باشد.


Google Analytics

به اختصار GA نامیده می­شود و یک ابزار آماری است که تعداد و انواع بازدیدکنندگان وب­‌سایت و نحوه استفاده از وب­‌سایت را اندازه­‌گیری می­کند. این محصول بر روی بسته تحلیلی Urchin 5 که گوگل در سال 2006 آن را خریداری نمود، ساخته شده است. این سرویس رایگان عرضه می­شود و فرآیند تحلیل را با استفاده از یک قطعه کد جاوا اسکریپت به نام Google Analytics Tracking Code با پیاده‌سازی در تگ صفحه وب انجام می­‌شود.

این کد با اولین بارگذاری صفحه در سیستم­ کاربران، به جمع آوری اطلاعات مورد نیاز پرداخته و برای پردازش به سرورهای GA باز پس می­فرستد. این کد با کمک Cookie مرورگر اطلاعات مورد نیاز را جمع آوری می­نماید.

سرویس Google Analytics


آشنایی با Google App Engine

GAE یک سکو به عنوان سرویس می­باشد و مبتنی بر ابر گوگل است و بر روی زیر ساخت­ گوگل مستقر شده است. 

این سرویس توسعه دهندگان را قادر می­سازد تا برنامه‌­های وب ایجاد کرده و بر روی سرور­های گوگل مستقر سازند و گوگل مدیریت زیرساخت را بر عهده گیرد و اعمالی مانند نظارت، برطرف کردن اشکالات احتمالی، خوشه بندی، مدیریت وهله‌سازی ماشین‌های مجازی و غیره را انجام دهد. برای اجرای یک برنامه در GAE ابتدا باید استانداردهای سکوی گوگل رعایت شود. این استانداردها دامنه برنامه­‌هایی که قابل اجرا می­باشند را بسیار محدود می‌­نماید و قابلیت حمل آن­ها را کاهش می­دهد.

محدودیت­‌هایی که این سکو ایجاد می­‌کند، با خود مزایایی را به همراه می­‌آورد که در زیر به آن­ها اشاره می­گردد:

  • وب سرویس­های پویا بر مبنای استانداردهای رایج
  • توسعه خودکار و توازن بار بین ماشین‌های وهله‌سازی شده که مورد استفاده وب سرویس است.
  • اعتبارسنجی با استفاده از API موجود در گوگل.
  • فضای ذخیره سازی ماندگار با قابلیت جستجو، مرتب سازی و مدیریت تراکنش.
  • صف کاری و زمان بندی کاری
  • محیط توسعه سمت مشتری( توسعه دهندگان ) برای شبیه سازی GAE در سیستم محلی.
  • پشتیبانی از محیط اجرا جاوا و پایتون.

هنگامی که یک برنامه در GAE مستقر گردید، با استفاده از نام دامنه دلخواه یا با استفاده از آدرس تجاری Google Apps قابل دستیابی است. موتور Google Apps در حال حاضر برنامه­‌هایی که در جاوا و پایتون نوشته شده است را پشتیبانی می­کند و علاوه بر آن از زبان­های ماشین مجازی جاوا و چندین چارچوب تحت وب پایتون که WSGI و CGI را پشتیبانی می­کنند نیز با محیط GAE سازگاری دارند.

برنامه­‌هایی که در GAE اجرا می­شوند از سیستم عامل مستقل هستند یا به گفته گوگل بر روی   Sand Box  اجرا می­شوند. این ویژگی GAE را قادر می­سازد، سیستم را بهینه کند تا تقاضاهای وب، با بار ترافیکی فعلی منطبق شوند. همچنین برنامه­‌ها را قادر می­سازد با امنیت بالاتری کار کنند، زیرا تنها می­توانند به کامپیوترهایی متصل شوند که آدرس‌های مشخصی دارند و سرویس­ها را با استفاده از پروتکل Http و یا Https از پورت­های شناخته شده پاسخ دهند. از طرف دیگر برنامه‌­ها نیز به این میزان محدود شده که تنها فایل­ها را بخوانند. آن‌ها حق نوشتن فایل به صورت مستقیم بر روی سیستم­ها را ندارند و برای دستیابی به داده، باید از ذخیره داده در Cache یا سرویس ماندگار دیگری استفاده نمایند.

GAE یک سیستم انبار داده توزیع شده دارد که از پرس و جوها و تراکنش­ها پشتیبانی می­نماید. این انبار داده غیر رابطه­‌ای است، اما اشیاء داده یا موجودیت­‌هایی که خصوصیات لازم را دارند، ذخیره می­‌نماید. به همین علت در پرس و جوها می­توان از فیلتر نوع موجودیت بهره برد و آن­ها را به ترتیب خصوصیات مرتب نمود.

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

  • مبلغ به ازای هر یک ساعت استفاده از CPU معادل 0.08 دلار
  • داده ذخیره شده به ازای هر گیگابایت در ماه معادل 0.18 دلار
  • پهنای باند خروجی به ازای هر گیگابایت معادل 0.12 دلار
  • پهنای باند ورودی رایگان
  • هزینه دریافت هر ایمیل معادل 0.0001 دلار 

به منظور ذخیره اطلاعات در منبع داده پایدار، از API استفاده می­گردد که به ازای تعداد تراکنش­هایی که تبادل می­گردد، هزینه پرداخت می­شود. از آنجایی که بنا به تعداد تبادلات و نوع حافظه پایداری که استفاده می­گردد، هزینه متغیر است، خواننده محترم برای رویت لیست مذکور به منبع ذکر شده، ارجاع داده می­شود.


  منبع سهمیه 
سهمیه پیش فرض رایگان به ازای هر برنامه   
  مصرف  CPU 
  28 ساعت به ازای هر برنامه در روز 
  منبع ذخیره پایدار داده 
1 گیگابایت به ازای هر برنامه در ماه
  پهنای باند ورودی 
  1 گیگابایت به ازای هر برنامه در روز 
  پهنای باند خروجی 
  1 گیگابایت به ازای هر برنامه در روز 
  تراکنش با منبع داده  Datastore 
  50 هزار تراکنش برای خواندن و نوشتن به ازای هر برنامه در ماه 
  تراکنش با منبع داده  Blobstore 
  5 گیگابایت به ازای هر برنامه در روز 
  ایمیل دریافتی 
  100 دریافت به ازای هر برنامه در روز 

نظرات مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
یک نکته‌ی مهم: روش صحیح به روز رسانی وابستگی‌های پروژه‌های NET Core.
اگر solution شما از چندین پروژه تشکیل شده‌است، برای به روز رسانی وابستگی‌های آن، به آخرین نگارش پایدار هر کدام، از ابزار سراسری « dotnet-outdated » استفاده کنید. به این ترتیب به صورت خودکار تمام وابستگی‌های پروژه‌های شما اسکن شده و به آخرین نگارش موجود ارتقاء پیدا می‌کنند.
برای نصب آن از دستور زیر استفاده کنید:
dotnet tool install --global dotnet-outdated
و یا برای به روز رسانی نگارش موجود آن، دستور زیر را اجرا کنید:
dotnet tool update --global dotnet-outdated
پس از آن، به ریشه‌ی solution خود، جائیکه فایل sln قرار دارد، از طریق خط فرمان وارد شده و دستور زیر را اجرا کنید:
dotnet outdated -u
مزیت مهم اینکار، به روز رسانی خودکار و یک دست تمام پروژه‌های یک solution، به آخرین وابستگی‌های پایدار آن‌ها است. به این ترتیب خطاهایی مانند «MissingMethodException» را در حین کار با اجزای غیرهماهنگ با هم، دریافت نخواهید کرد.
اشتراک‌ها
قسمت 5 ام از سری ویدیو های کانکارنسی و بررسی Task and Thread Pool

قسمت 5ام از سری ویدیو‌های کانکارنسی داریم کم کم به انتهای ویدیو‌ها میرسیم و در این ویدیو درمورد بکگراند ترد‌ها و فرگراند ترد‌ها صحبت کردیم، به طور کامل فضای تسک و ترد پول رو بررسی و باز کردیم و در انتها یه نگاهی هم به فضای async, await انداختیم که در ادامه ویدیو‌ها بیشتر صحبت میکنیم.

0:00 last session 

03:00 Background and foreground thread 

06:50 CLR 

07:19 Thread Pool 

11:00 Task 

13:30 Task Result 

16:40 Long Running Tasks 

قسمت 5 ام از سری ویدیو های کانکارنسی و بررسی Task and Thread Pool
نظرات نظرسنجی‌ها
وضعیت Blazor WebAssembly را چطور ارزیابی می‌کنید؟
ما الآن تو ۲ پروژه یکی کوچک و یکی بزرگ ازش داریم استفاده می‌کنیم و به نظرم خودش معماری جالبی داره، امکانات Telerik براش خیلی کامله، استفاده از امکانات C# .NET هم واقعا خوبه
بدی اش همون کندی هست که تو dotnet 6 با AOT حل میشه + مسئله سایز که یه Spa ساده میشه ۳ مگ و یه Spa پیچیده با کلی کتابخونه کمکی تا ۷ مگ می‌ره
مطالب
معرفی یک ابزار گزارشگیری رایگان مخصوص WPF

تا صحبت از گزارشگیری به میان بیاید احتمالا معرفی ابزارهای تجاری مانند Reporting services ، کریستال ریپورت، stimulsoft.com ، fast-report.com و امثال آن درصدر لیست توصیه کنندگان و مشاوران قرار خواهند داشت. اما خوب برای ایجاد یک گزارشگیری ساده حتما نیازی نیست تا به این نوع ابزارهای تجاری مراجعه کرد. ابزار رایگان و سورس باز جالبی هم در این باره جهت پروژه‌های WPF در دسترس است:



در ادامه در طی یک مثال قصد داریم از این کتابخانه استفاده کنیم:

1) تنظیم وابستگی‌ها
پس از دریافت کتابخانه فوق، ارجاعات زیر باید به پروژه شما اضافه شوند:
CodeReason.Reports.dll (از پروژه فوق) و ReachFramework.dll (جزو اسمبلی‌های استاندارد دات نت است)

2) تهیه منبع داده‌ گزارش
کتابخانه‌ی فوق به صورت پیش فرض با DataTable‌ کار می‌کند. بنابراین کوئری‌های شما یا باید خروجی DataTable داشته باشد یا باید از یک سری extension methods برای تبدیل IEnumerable به DataTable استفاده کرد (در پروژه پیوست شده در پایان مطلب، این موارد موجود است).
برای مثال فرض کنید می‌خواهیم رکوردهایی را از نوع کلاس Product زیر در گزارش نمایش دهیم:

namespace WpfRptTests.Model
{
public class Product
{
public string Name { set; get; }
public int Price { set; get; }
}
}
3) تعریف گزارش
الف) اضافه کردن فایل تشکیل دهنده ساختار و ظاهر گزارش
گزارش‌‌های این کتابخانه مبتنی است بر اشیاء FlowDocument استاندارد WPF . بنابراین از منوی پروژه گزینه‌ی Add new item در قسمت WPF آن یک FlowDocument جدید را به پروژه اضافه کنید ( باید دقت داشت که Build action این فایل باید به Content تنظیم گردد). ساختار ابتدایی این FlowDocument به صورت زیر خواهد بود که به آن FlowDirection و FontFamily مناسب جهت گزارشات فارسی اضافه شده است. همچنین فضای نام مربوط به کتابخانه‌ی گزارشگیری CodeReason.Reports نیز باید اضافه گردد.
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="RightToLeft" FontFamily="Tahoma"
xmlns:xrd="clr-namespace:CodeReason.Reports.Document;assembly=CodeReason.Reports"
PageHeight="29.7cm" PageWidth="21cm" ColumnWidth="21cm">

</FlowDocument>

مواردی که در ادامه ذکر خواهند شد محتوای این گزارش را تشکیل می‌دهند:
ب) مشخص سازی خواص گزارش

<xrd:ReportProperties>
<xrd:ReportProperties.ReportName>SimpleReport</xrd:ReportProperties.ReportName>
<xrd:ReportProperties.ReportTitle>گزارش از محصولات</xrd:ReportProperties.ReportTitle>
</xrd:ReportProperties>
در اینجا ReportName و ReportTitle باید مقدار دهی شوند (دو dependency property که در کتابخانه‌ی CodeReason.Reports تعریف شده‌اند)

ج) مشخص سازی Page Header و Page Footer
اگر می‌خواهید عباراتی در بالا و پایین تمام صفحات گزارش تکرار شوند می‌توان از SectionReportHeader و SectionReportFooter این کتابخانه به صورت زیر استفاده کرد:
    <xrd:SectionReportHeader PageHeaderHeight="2" Padding="10,10,10,0" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportHeader>

<xrd:SectionReportFooter PageFooterHeight="2" Padding="10,0,10,10" FontSize="12">
<Table CellSpacing="0">
<Table.Columns>
<TableColumn Width="*" />
<TableColumn Width="*" />
</Table.Columns>
<TableRowGroup>
<TableRow>
<TableCell>
<Paragraph>
نام کاربر:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Right">
صفحه
<xrd:InlineContextValue PropertyName="PageNumber" FontWeight="Bold" /> از
<xrd:InlineContextValue PropertyName="PageCount" FontWeight="Bold" />
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>
</Table>
</xrd:SectionReportFooter>

دو نکته در اینجا حائز اهمیت هستند: xrd:InlineDocumentValue و xrd:InlineContextValue
InlineDocumentValue را می‌توان در کد‌های برنامه به صورت سفارشی اضافه کرد. بنابراین هر جایی که نیاز بود مقدار ثابتی از طریق کد نویسی به گزارش تزریق و اضافه شود می‌توان از InlineDocumentValue استفاده کرد. برای مثال در کدهای ViewModel برنامه که در ادامه ذکر خواهد شد دو مقدار PrintDate و RptBy به صورت زیر تعریف و مقدار دهی شده‌اند:
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");
برای مشاهده مقادیر مجاز مربوط به InlineContextValue به فایل ReportContextValueType.cs سورس کتابخانه مراجعه کنید که شامل PageNumber, PageCount, ReportName, ReportTitle است و توسط CodeReason.Reports به صورت پویا تنظیم خواهد شد.

د) مشخص سازی ساختار تولیدی گزارش

<Section Padding="80,10,40,10" FontSize="12">
<Paragraph FontSize="24" TextAlignment="Center" FontWeight="Bold">
<xrd:InlineContextValue PropertyName="ReportTitle" />
</Paragraph>
<Paragraph TextAlignment="Center">
گزارش از لیست محصولات در تاریخ:
<xrd:InlineDocumentValue PropertyName="PrintDate" Format="dd.MM.yyyy HH:mm:ss" />
توسط:
<xrd:InlineDocumentValue PropertyName="RptBy" Format="dd.MM.yyyy HH:mm:ss" />
</Paragraph>
<xrd:SectionDataGroup DataGroupName="ItemList">
<Table CellSpacing="0" BorderBrush="Black" BorderThickness="0.02cm">
<Table.Resources>
<!-- Style for header/footer rows. -->
<Style x:Key="headerFooterRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Background" Value="LightGray"/>
</Style>

<!-- Style for data rows. -->
<Style x:Key="dataRowStyle" TargetType="{x:Type TableRowGroup}">
<Setter Property="FontSize" Value="12"/>
</Style>

<!-- Style for data cells. -->
<Style TargetType="{x:Type TableCell}">
<Setter Property="Padding" Value="0.1cm"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="0.01cm"/>
</Style>
</Table.Resources>

<Table.Columns>
<TableColumn Width="0.8*" />
<TableColumn Width="0.2*" />
</Table.Columns>
<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>نام محصول</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>قیمت</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

<TableRowGroup Style="{StaticResource dataRowStyle}">
<xrd:TableRowForDataTable TableName="Product">
<TableCell>
<Paragraph>
<xrd:InlineTableCellValue PropertyName="Name" />
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<xrd:InlineTableCellValue PropertyName="Price" AggregateGroup="Group1" />
</Paragraph>
</TableCell>
</xrd:TableRowForDataTable>
</TableRowGroup>

<TableRowGroup Style="{StaticResource headerFooterRowStyle}">
<TableRow>
<TableCell>
<Paragraph TextAlignment="Right">
<Bold>جمع کل</Bold>
</Paragraph>
</TableCell>
<TableCell>
<Paragraph TextAlignment="Center">
<Bold>
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" />
</Bold>
</Paragraph>
</TableCell>
</TableRow>
</TableRowGroup>

</Table>

<Paragraph TextAlignment="Center" Margin="5">
در این گزارش
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Count"
EmptyValue="هیچ"
FontWeight="Bold" /> محصول با جمع کل قیمت
<xrd:InlineAggregateValue AggregateGroup="Group1"
AggregateValueType="Sum"
EmptyValue="0"
FontWeight="Bold" /> وجود دارند.
</Paragraph>
</xrd:SectionDataGroup>
</Section>
برای اینکه بتوان این قسمت‌ها را بهتر توضیح داد، نیاز است تا تصاویر مربوط به خروجی این گزارش نیز ارائه شوند:




در ابتدا توسط دو پاراگراف، عنوان گزارش و یک سطر زیر آن نمایش داده شده‌اند. بدیهی است هر نوع شیء و فرمت مجاز در FlowDocument را می‌توان در این قسمت نیز قرار داد.
سپس یک SectionDataGroup جهت نمایش لیست آیتم‌ها اضافه شده و داخل آن یک جدول که بیانگر ساختار جدول نمایش رکوردهای گزارش می‌باشد، ایجاد گردیده است.
سه TableRowGroup در این جدول تعریف شده‌اند.
TableRowGroup های اولی و آخری دو سطر اول و آخر جدول گزارش را مشخص می‌کنند (سطر عناوین ستون‌ها در ابتدا و سطر جمع کل در پایان گزارش)
از TableRowGroup میانی برای نمایش رکوردهای مرتبط با نام جدول مورد گزارشگیری استفاده شده است. توسط TableRowForDataTable آن نام این جدول باید مشخص شود که در اینجا همان نام کلاس مدل برنامه است. به کمک InlineTableCellValue، خاصیت‌هایی از این کلاس را که نیاز است در گزارش حضور داشته باشند، ذکر خواهیم کرد. نکته‌ی مهم آن AggregateGroup ذکر شده است. توسط آن می‌توان اعمال جمع، محاسبه تعداد، حداقل و حداکثر و امثال آن‌را که در فایل InlineAggregateValue.cs سورس کتابخانه ذکر شده‌اند، به فیلدهای مورد نظر اعمال کرد. برای مثال می‌خواهیم جمع کل قیمت را در پایان گزارش نمایش دهیم به همین جهت نیاز بود تا یک AggregateGroup را برای این منظور تعریف کنیم.
از این AggregateGroup در سومین TableRowGroup تعریف شده به کمک xrd:InlineAggregateValue جهت نمایش جمع نهایی استفاده شده است.
همچنین اگر نیاز بود در پایان گزارش اطلاعات بیشتری نیز نمایش داده شود به سادگی می‌توان با تعریف یک پاراگراف جدید، اطلاعات مورد نظر را نمایش داد.

4) نمایش گزارش تهیه شده
نمایش این گزارش بسیار ساده است. View برنامه به صورت زیر خواهد بود:
<Window x:Class="WpfRptTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CodeReason.Reports.Controls;assembly=CodeReason.Reports"
xmlns:vm="clr-namespace:WpfRptTests.ViewModel"
Title="MainWindow" WindowState="Maximized" Height="350" Width="525">
<Window.Resources>
<vm:ProductViewModel x:Key="vmProductViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vmProductViewModel}}">
<c:BusyDecorator IsBusyIndicatorHidden="{Binding RptGuiModel.IsBusyIndicatorHidden}">
<DocumentViewer Document="{Binding RptGuiModel.Document}" />
</c:BusyDecorator>
</Grid>
</Window>

تعریف ابتدایی RptGuiModel به صورت زیر است (جهت مشخص سازی مقادیر IsBusyIndicatorHidden و Document در حین بایندینگ اطلاعات):

using System.ComponentModel;
using System.Windows.Documents;

namespace WpfRptTests.Model
{
public class RptGuiModel
{
public IDocumentPaginatorSource Document { get; set; }
public bool IsBusyIndicatorHidden { get; set; }
}
}
و این View اطلاعات خود را از ViewModel زیر دریافت خواهد نمود:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using CodeReason.Reports;
using WpfRptTests.Helper;
using WpfRptTests.Model;

namespace WpfRptTests.ViewModel
{
public class ProductViewModel
{
#region Constructors (1)

public ProductViewModel()
{
RptGuiModel = new RptGuiModel();
if (Stat.IsInDesignMode) return;
//انجام عملیات نمایش گزارش در یک ترد دیگر جهت قفل نشدن ترد اصلی برنامه
showReportAsync();
}

#endregion Constructors

#region Properties (1)

public RptGuiModel RptGuiModel { set; get; }

#endregion Properties

#region Methods (3)

// Private Methods (3)

private static List<Product> getProducts()
{
var products = new List<Product>();
for (var i = 0; i < 100; i++)
products.Add(new Product { Name = string.Format("Product{0}", i), Price = i });

return products;
}

private void showReport()
{
try
{
//Show BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = false;

var reportDocument =
new ReportDocument
{
XamlData = File.ReadAllText(@"Report\SimpleReport.xaml"),
XamlImagePath = Path.Combine(Environment.CurrentDirectory, @"Report\")
};

var data = new ReportData();

// تعریف متغیرهای دلخواه و مقدار دهی آن‌ها
data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
data.ReportDocumentValues.Add("RptBy", "وحید");

// استفاده از یک سری اطلاعات آزمایشی به عنوان منبع داده
data.DataTables.Add(getProducts().ToDataTable());

var xps = reportDocument.CreateXpsDocument(data);
//انقیاد آن به صورت غیر همزمان در ترد اصلی برنامه
DispatcherHelper.DispatchAction(
() => RptGuiModel.Document = xps.GetFixedDocumentSequence()
);
}
catch (Exception ex)
{
//وجود این مورد ضروری است زیرا بروز استثناء در یک ترد به معنای خاتمه آنی برنامه است
//todo: log errors
}
finally
{
//Hide BusyIndicator
RptGuiModel.IsBusyIndicatorHidden = true;
}
}

private void showReportAsync()
{
var thread = new Thread(showReport);
thread.SetApartmentState(ApartmentState.STA); //for DocumentViewer
thread.Start();
}

#endregion Methods
}
}

توضیحات:
برای اینکه حین نمایش گزارش، ترد اصلی برنامه قفل نشود، از ترد استفاده شد و استفاده ترد به همراه DocumentViewer کمی نکته دار است:
- ترد تعریف شده باید از نوع STA باشد که در متد showReportAsync مشخص شده است.
- حین بایندیگ Document تولید شده توسط کتابخانه‌ی گزارشگیری به خاصیت Document کنترل، حتما باید کل عملیات در ترد اصلی برنامه صورت گیرد که سورس کلاس DispatcherHelper را در فایل پیوست خواهید یافت.

کل عملیات این ViewModel در متد showReport رخ می‌دهد، ابتدا فایل گزارش بارگذاری می‌شود، سپس متغیرهای سفارشی مورد نظر تعریف و مقدار دهی خواهند شد. در ادامه یک سری داده آزمایشی تولید و به DataTables گزارش ساز اضافه می‌شوند. در پایان XPS Document متناظر آن تولید شده و به کنترل نمایشی برنامه بایند خواهد شد.

دریافت سورس این مثال

مطالب
افزودن تصدیق ایمیل به ASP.NET Identity در MVC 5
در پست قبلی نحوه سفارشی کردن پروفایل کاربران در ASP.NET Identity را مرور کردیم. اگر بیاد داشته باشید یک فیلد آدرس ایمیل به کلاس کاربر اضافه کردیم. در این پست از این فیلد استفاده میکنیم تا در پروسه ثبت نام ایمیل‌ها را تصدیق کنیم. بدین منظور پس از ثبت نام کاربران یک ایمیل فعالسازی برای آنها ارسال می‌کنیم که حاوی یک لینک است. کاربران با کلیک کردن روی این لینک پروسه ثبت نام خود را تایید می‌کنند و می‌توانند به سایت وارد شوند. پیش از تایید پروسه ثبت نام، کاربران قادر به ورود نیستند.


در ابتدا باید اطلاعات کلاس کاربر را تغییر دهید تا دو فیلد جدید را در بر گیرد. یک فیلد شناسه تایید (confirmation token) را ذخیره می‌کند، و دیگری فیلدی منطقی است که مشخص می‌کند پروسه ثبت نام تایید شده است یا خیر. پس کلاس ApplicationUser  حالا باید بدین شکل باشد.
public class ApplicationUser : IdentityUser
{
    public string Email { get; set; }
    public string ConfirmationToken { get; set; }
    public bool IsConfirmed { get; set; }
}
اگر پیش از این کلاس ApplicationUser را تغییر داده اید، باید مهاجرت‌ها را فعال کنید و دیتابیس را بروز رسانی کنید. حالا می‌توانیم از این اطلاعات جدید در پروسه ثبت نام  استفاده کنیم و برای کاربران ایمیل‌های تاییدیه را بفرستیم.
private string CreateConfirmationToken()
{
    return ShortGuid.NewGuid();
}
 
private void SendEmailConfirmation(string to, string username, string confirmationToken)
{
    dynamic email = new Email("RegEmail");
    email.To = to;
    email.UserName = username;
    email.ConfirmationToken = confirmationToken;
    email.Send();
}
 
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        string confirmationToken = CreateConfirmationToken();
        var user = new ApplicationUser()
        {
            UserName = model.UserName,
            Email = model.Email,
            ConfirmationToken = confirmationToken, 
                IsConfirmed = false };
        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            SendEmailConfirmation(model.Email, model.UserName, confirmationToken);
            return RedirectToAction("RegisterStepTwo", "Account");
        }
        else
        {
            AddErrors(result);
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
}
برای تولید شناسه‌های تایید (tokens) از کلاسی بنام ShortGuid استفاده شده است. این کلاس یک مقدار GUID را encode می‌کند که در نتیجه آن مقدار خروجی کوتاه‌تر بوده و برای استفاده در URL‌ها ایمن است. کد این کلاس را از این وبلاگ گرفته ام. پس از ایجاد حساب کاربری باید شناسه تولید شده را به آن اضافه کنیم و مقدار فیلد IsConfirmed را به false تنظیم کنیم. برای تولید ایمیل‌ها من از Postal استفاده می‌کنم. Postal برای ساختن ایمیل‌های دینامیک شما از موتور Razor استفاده می‌کند. می‌توانید ایمیل‌های ساده (plain text) یا HTML بسازید، عکس و فایل در آن درج و ضمیمه کنید و امکانات بسیار خوب دیگر. اکشن متد RegisterStepTwo تنها کاربر را به یک View هدایت می‌کند که پیامی به او نشان داده می‌شود.
بعد از اینکه کاربر ایمیل را دریافت کرد و روی لینک تایید کلیک کرد به اکشن متد RegisterConfirmation باز می‌گردیم.
private bool ConfirmAccount(string confirmationToken)
{
    ApplicationDbContext context = new ApplicationDbContext();
    ApplicationUser user =  context.Users.SingleOrDefault(u => u.ConfirmationToken == confirmationToken);
    if (user != null)
    {
        user.IsConfirmed = true;
        DbSet<ApplicationUser> dbSet = context.Set<ApplicationUser>();
        dbSet.Attach(user);
        context.Entry(user).State = EntityState.Modified;
        context.SaveChanges();
 
        return true;
    }
    return false;
}
 
[AllowAnonymous]
public ActionResult RegisterConfirmation(string Id)
{
    if (ConfirmAccount(Id))
    {
        return RedirectToAction("ConfirmationSuccess");
    }
    return RedirectToAction("ConfirmationFailure");
}
متد ConfirmAccount سعی می‌کند کاربری را در دیتابیس پیدا کند که شناسه تاییدش با مقدار دریافت شده از URL برابر است. اگر این کاربر پیدا شود، مقدار خاصیت IsConfirmed را به true تغییر می‌دهیم و همین مقدار را به تابع باز می‌گردانیم. در غیر اینصورت false بر می‌گردانیم. اگر کاربر تایید شده است، می‌تواند به سایت وارد شود. برای اینکه مطمئن شویم کاربران پیش از تایید ایمیل شان نمی‌توانند وارد سایت شوند، باید اکشن متد Login را کمی تغییر دهیم.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindAsync(model.UserName, model.Password);
        if (user != null && user.IsConfirmed)
        {
            await SignInAsync(user, model.RememberMe);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
}
تنها کاری که می‌کنیم این است که به دنبال کاربری می‌گردیم که فیلد IsConfirmed آن true باشد. اگر مقدار این فیلد false باشد کاربر را به سایت وارد نمی‌کنیم و پیغام خطایی نمایش می‌دهیم.
همین. این تمام چیزی بود که برای اضافه کردن تصدیق ایمیل به اپلیکیشن خود نیاز دارید. از آنجا که سیستم ASP.NET Identity با Entity Framework مدیریت می‌شود و با مدل Code First ساخته شده، سفارشی کردن اطلاعات کاربران و سیستم عضویت ساده‌تر از همیشه است.



توضیحاتی درباره کار با Postal

اگر به متد SendEmailConfirmation دقت کنید خواهید دید که آبجکتی از نوع Email می‌سازیم (که در اسمبلی‌های Postal وجود دارد) و از آن برای ارسال ایمیل استفاده می‌کنیم. عبارت "RegEmail" نام نمایی است که باید برای ساخت ایمیل استفاده شود. این متغیر از نوع dynamic است، مانند خاصیت ViewBag. بدین معنا که می‌توانید مقادیر مورد نظر خود را بصورت خواص دینامیک روی این آبجکت تعریف کنید. از آنجا که Postal از موتور Razor استفاده می‌کند، بعدا در View ایمیل خود می‌توانید به این مقادیر دسترسی داشته باشید.
در پوشه Views پوشه جدیدی بنام Emails بسازید. سپس یک فایل جدید با نام RegEmail.cshtml در آن ایجاد کنید. کد این فایل را با لیست زیر جایگزین کنید.
To: @ViewBag.To
From: YOURNAME@gmail.com
Subject: Confirm your registration

Hello @ViewBag.UserName,
Please confirm your registration by following the link bellow.

@Html.ActionLink(Url.Action("RegisterConfirmation", "Account", new { id = @ViewBag.ConfirmationToken }), "RegisterConfirmation", "Account", new { id = @ViewBag.ConfirmationToken }, null)
این فایل، قالب ایمیل‌های شما خواهد بود. ایمیل‌ها در حال حاظر بصورت plain text ارسال می‌شوند. برای اطلاعات بیشتر درباره ایمیل‌های HTML و امکانات پیشرفته‌تر به سایت پروژه Postal  مراجعه کنید.

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

  • ViewBag.To آدرس ایمیل گیرنده را نشان می‌دهد.
  • ViewBag.UserName نام کاربر جاری را نمایش می‌دهد.
  • ViewBag.ConfirmationToken شناسه تولید شده برای تایید کاربر است.
در این قالب لینکی به متد RegisterConfirmation در کنترلر Account وجود دارد که شناسه تایید را نیز با پارامتری بنام id انتقال می‌دهد.

یک فایل ViewStart.cshtml_ هم در این پوشه بسازید و کد آن را با لیست زیر جایگزین کنید.
@{ Layout = null; /* Overrides the Layout set for regular page views. */ }