مطالب
فعال سازی و پردازش صفحات پویای افزودن، ویرایش و حذف رکوردهای jqGrid در ASP.NET MVC
پیشنیاز این بحث مطالعه‌ی مطلب «صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC» است و در اینجا جهت کوتاه شدن بحث، صرفا به تغییرات مورد نیاز جهت اعمال بر روی مثال اول اکتفاء خواهد شد.


تغییرات مورد نیاز جهت فعال سازی ویرایش، حذف و افزودن رکوردهای jqGrid

می‌خواهیم در بدو نمایش گرید، یک ستون خاص دارای دکمه‌های ویرایش و حذف ظاهر شوند:


برای اینکار تنها کافی است در انتهای ستون‌های تعریف شده، یک ستون خاص را با formatter مساوی actions ایجاد کنیم:
                colModel: [
                    {
                        // سایر ستون‌ها
                        name: 'myac', width: 80, fixed: true, sortable: false,
                        resize: false, formatter: 'actions',
                        formatoptions: {
                            keys: true
                        }
                    }
                ],
برای اینکه دکمه‌های ویرایش و حذف ردیف‌های آن عمل کنند:


نیاز است تعاریف سایر ستون‌هایی را که باید قابلیت ویرایش داشته باشند، به نحو ذیل تغییر دهیم:
                colModel: [
                    {
                        name: 'Id', index: 'Id', align: 'right', width: 70,
                        editable: false
                    },
                    {
                        name: 'Name', index: 'Name', align: 'right', width: 100,
                        editable: true, edittype: 'text',
                        editoptions: {
                            maxlength: 40
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Supplier.Id', index: 'Supplier.Id', align: 'right', width: 110,
                        editable: true, edittype: 'select',
                        editoptions: {
                            dataUrl: '@Url.Action("SuppliersSelect","Home")'
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Category.Id', index: 'Category.Id', align: 'right', width: 110,
                        editable: true, edittype: 'select',
                        editoptions: {
                            dataUrl: '@Url.Action("CategoriesSelect","Home")'
                        },
                        editrules: {
                            required: true
                        }
                    },
                    {
                        name: 'Price', index: 'Price', align: 'center', width: 100,
                        formatter: 'currency',
                        formatoptions:
                        {
                            decimalSeparator: '.',
                            thousandsSeparator: ',',
                            decimalPlaces: 2,
                            prefix: '$'
                        },
                        editable: true, edittype: 'text',
                        editrules: {
                            required: true,
                            number: true,
                            minValue: 0
                        }
                    },
                    {
                        name: 'myac', width: 80, fixed: true, sortable: false,
                        resize: false, formatter: 'actions',
                        formatoptions: {
                            keys: true
                        }
                    }
                ],
- در اینجا هر ستونی که دارای خاصیت editable مساوی true است، قابلیت ویرایش پیدا می‌کند.
- edittype آن بیانگر کنترلی است که باید حین ویرایش آن سلول خاص ظاهر شود. برای مثال اگر text باشد، یک text box و اگر مانند حالت Supplier.Id مساوی select تعریف شود، یک drop down را ظاهر خواهد کرد. برای مقدار دهی این drop down می‌توان editoptions و سپس dataUrl آن‌را مقدار دهی نمود.
        public ActionResult SuppliersSelect()
        {
            var list = ProductDataSource.LatestProducts;
            var suppliers = list.Select(x => new SelectListItem
            {
                Text = x.Supplier.CompanyName,
                Value = x.Supplier.Id.ToString(CultureInfo.InvariantCulture)
            }).ToList();
            return PartialView("_SelectPartial", suppliers);
        }
در مثال فوق، این dataUrl به اکشن متد SuppliersSelect اشاره می‌کند که نهایتا لیستی از تولید کننده‌ها را توسط partial view ذیل بازگشت می‌دهد:
 @model IList<SelectListItem>

@Html.DropDownList("srch", Model)
در کل مقادیر قابل تنظیم در اینجا شامل  text، textarea، select، checkbox، password، button، image، file و custom هستند.
- خاصیت editrules، برای مباحث اعتبارسنجی اطلاعات ورودی توسط کاربر پیش بینی شده‌است. برای مثال اگر required: true در آن تنظیم شود، کاربر مجبور به تکمیل این سلول خاص خواهد بود. در اینجا خواصی مانند number و integer از نوع bool، خاصیت‌های minValue و maxValue از نوع عددی، email, url, date, time از نوع bool و custom قابل تنظیم است (مثال‌های حالت custom را در منابع انتهای بحث می‌توانید مطالعه کنید).
- پس از اینکه مشخص شدند کدامیک از ستون‌ها باید قابلیت ویرایش داشته باشند، مسیری که باید اطلاعات نهایی را به سرور ارسال کند، توسط خاصیت editurl مشخص می‌شود:
$('#list').jqGrid({
   caption: "آزمایش چهارم",
   //url from wich data should be requested
   url: '@Url.Action("GetProducts","Home")',
   //url for edit operation
   editurl: '@Url.Action("EditProduct","Home")',
اکشن متد متناظر با این آدرس یک چنین شکلی را می‌تواند داشته باشد:
        [HttpPost]
        public ActionResult EditProduct(Product postData)
        {
            //todo: Edit product based on postData

            return Json(true);
        }
- تعاریف مسیرهای ارسال اطلاعات Add و Delete، در قسمت تنظیمات navGrid باید ذکر شوند:
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: false, search: false },
                //edit options
                {},
                //add options
                { width: 'auto', url: '@Url.Action("AddProduct","Home")' },
                //delete options
                { url: '@Url.Action("DeleteProduct","Home")' }
                );
امضای این اکشن متدها نیز بسیار شبیه به اکشن متد ویرایش است:
        [HttpPost]
        public ActionResult DeleteProduct(string id)
        {
            //todo: Delete product
            return Json(true);
        }

        [HttpPost]
        public ActionResult AddProduct(Product postData)
        {
            //todo: Add product to repository
            return Json(true);
        }
- حالت ویرایش و حذفی که تا اینجا بررسی شد (ستون actions)، جزو خواص توکار این گرید است. اگر بخواهیم آن‌ها را دستی فعال کنیم (جهت اطلاعات عمومی) می‌توان از فراخوانی متد ذیل نیز کمک گرفت:
        var lastSel;
        function inlineEdit() {
            $('input[name=rdEditApproach]').attr('disabled', true);
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: false, search: false },
                //edit options
                {},
                //add options
                { width: 'auto', url: '@Url.Action("AddProduct","Home")' },
                //delete options
                { url: '@Url.Action("DeleteProduct","Home")' }
                );
            //add onSelectRow event to support inline edit
            $('#list').setGridParam({
                onSelectRow: function (id) {
                    if (id && id != lastSel) {
                        //save changes in row
                        $('#list').saveRow(lastSel, false);
                        lastSel = id;
                    }
                    //trigger inline edit for row
                    $('#list').editRow(id, true);
                }
            });
        };
در اینجا ابتدا همان تنظیمات مسیرهای Add و Delete انجام شده‌است. سپس با فراخوانی دستی متد editRow در زمان کلیک بر روی یک ردیف، همان کاری را که ستون actions در جهت فعال سازی خودکار حالت ویرایش سلول‌ها انجام می‌دهد، می‌توان شبیه سازی کرد. متد saveRow نیز کار ارسال اطلاعات تغییر کرده را به سرور انجام می‌دهد.
- برای فعال سازی خودکار فرم‌های افزودن رکوردها و یا ویرایش ردیف‌های موجود می‌توان از فراخوانی متد formEdit ذیل کمک گرفت:
        function formEdit() {
            $('input[name=rdEditApproach]').attr('disabled', true);
            $('#list').navGrid(
                '#pager',
                //enabling buttons
                { add: true, del: true, edit: true, search: false },
                //edit option
                {
                    width: 'auto', checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    }
                },
                //add options
                {
                    width: 'auto', url: '@Url.Action("AddProduct","Home")',
                    reloadAfterSubmit: false, checkOnUpdate: true, checkOnSubmit: true,
                    beforeShowForm: function (form) {
                        centerDialog(form, $('#list'));
                    }
                },
                //delete options
                {
                    url: '@Url.Action("DeleteProduct","Home")', reloadAfterSubmit: false
                })
            .jqGrid('navButtonAdd', "#pager", {
                caption: "حذف ردیف‌های انتخابی", title: "Delete Toolbar", buttonicon: 'ui-icon ui-icon-trash',
                onClickButton: function () {
                    var idsList = jQuery("#list").jqGrid('getGridParam', 'selarrrow');
                    alert(idsList);
                    //jQuery("#list").jqGrid('delGridRow',idsList,{reloadAfterSubmit:false});
                }
            });
        };

        function centerDialog(form, grid) {
            var dlgDiv = $("#editmod" + grid[0].id);
            var parentDiv = dlgDiv.parent(); // div#gbox_list
            var dlgWidth = dlgDiv.width();
            var parentWidth = parentDiv.width();
            var dlgHeight = dlgDiv.height();
            var parentHeight = parentDiv.height();
            var parentTop = parentDiv.offset().top;
            var parentLeft = parentDiv.offset().left;
            dlgDiv[0].style.top =  Math.round(  parentTop  + (parentHeight-dlgHeight)/2  ) + "px";
            dlgDiv[0].style.left = Math.round(  parentLeft + (parentWidth-dlgWidth  )/2 )  + "px";
        }
ابتدای تنظیمات آن، شاهد add: true, del: true, edit: true هستید. این مورد سبب می‌شود تا در فوتر گرید، سه دکمه‌ی افزودن، ویرایش و حذف ردیف‌ها ظاهر شوند:


با کلیک بر روی دکمه‌ی افزودن ردیف جدید، صفحه‌ی ذیل به صورت خودکار تولید می‌شود:


و با کلیک بر روی دکمه‌ی ویرایش ردیفی انتخاب شده، صفحه‌ی ویرایش آن ردیف به همراه مقادیر سلول‌های آن ظاهر خواهند شد:


تنظیمات قسمت‌های Add و Delete ویرایش توسط فرم‌ها، با حالت ویرایش داخل ردیفی آنچنان تفاوتی ندارد. فقط در اینجا پیش از نمایش فرم، از متد centerDialog برای نمایش صفحات افزودن و ویرایش رکوردها در وسط صفحه، استفاده شده‌است. توسط checkOnUpdate: true, checkOnSubmit: true سبب خواهیم شد تا اگر کاربر مقادیر موجود فرمی را تغییر داده‌است و سعی در بستن فرم، بدون ذخیره سازی اطلاعات کند، پیغام هشدار دهنده‌ای به او نمایش داده شود که آیا می‌خواهید تغییرات را ذخیره کنید یا خیر؟


- در انتهای متد formEdit، به کمک متد jqGrid و پارامتر navButtonAdd یک دکمه‌ی سفارشی را نیز اضافه کرده‌ایم. اگر به ستون پس از شماره‌های خودکار ردیف‌ها، در سمت راست گرید دقت کنید، یک سری chekbox قابل مشاهده هستند. برای فعال سازی خودکار آن‌ها کافی است خاصیت multiselect گرید به true تنظیم شود. اکنون برای دسترسی به این ستون‌های انتخاب شده، می‌توان از متد jqGrid به همراه پارامترهای getGridParam و selarrrow استفاده کرد. خروجی آن، لیست idهای ستون‌ها است.


برای مطالعه بیشتر

Common Editing Properties
Inline Editing
Form Editing
Cell Editing


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
jqGrid04.zip
مطالب
کار با دیتاتایپ JSON در MySQL - قسمت چهارم
MySQL قادر به ایندکس کردن ستون‌های JSON نمی‌باشد. برای حل این مشکل میتوانیم از generated columnها استفاده کنیم. منظور، ایجاد ستون‌هایی است که مقدارشان به صورت محاسبه شده و براساس ستون‌های دیگر میباشد؛ به عنوان مثال جدول کاربران زیر را در نظر بگیرید:
CREATE TABLE `Users` (
  id int NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(255) NOT NULL,
  last_name VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL,
  gender ENUM('Male','Female') NOT NULL,
  PRIMARY KEY (`id`)
)
برای کوئری گرفتن full name در حالت معمول میتوانیم از تابع CONCAT استفاده کنیم:
SELECT 
    *, CONCAT(first_name, '', last_name) AS full_name
FROM
    Users;
اما توسط generated columns میتوانیم یک ستون را به جدول کاربران اضافه کنیم که مقدارش براساس دو فیلد first_name و last_name محاسبه و مقدار دهی شود:
ALTER TABLE Users
ADD COLUMN full_name TEXT GENERATED ALWAYS 
AS (CONCAT(first_name, ' ', last_name))
همانطور که مشاهده میکنید از سینتکس GENERATE ALWAYS برای ایجاد generated column استفاده شده‌است. در MySQL دو نوع generated column وجود دارد: STORED و VIRTUAL؛ تفاوت آنها نیز در نحوه ذخیره‌سازی است. در حالت VIRTUAL که حالت پیش‌فرض است، مقادیر ذخیره نمیشوند؛ بلکه به صورت on the fly محاسبه و در خروجی نمایش داده خواهند شد. در حالیکه نوع STORED همانطور که از نامش پیداست، ذخیره خواهند شد؛ در نتیجه قابلیت ایندکس‌گذاری را دارد. برای تعیین نوع ستون نیز سینتکس آن اینگونه خواهد بود:
ALTER TABLE Users
ADD COLUMN full_name TEXT GENERATED ALWAYS 
AS (CONCAT(first_name, ' ', last_name)) STORED

همچنین لازم به ذکر است که حین استفاده از generated columns باید نکات زیر را در نظر داشته باشید:
  • generated columnsها نمیتوانند شامل subqueries, parameters, variables, stored procedure, user-defined functions باشند.
  • بر روی یک ستون generated نمیتوان AUTO_INCREMENT گذاشت یا اینکه از یک ستون AUTO_INCREMENT برای محاسبه generated column استفاده کرد.
  • کلیدهای خارجی‌ای که در generated columnsها استفاده میشوند، قابلیت استفاده از CASCADE, SET NULL, or SET DEFAULT as ON UPDATE or ON DELETE را نخواهند داشت.

در ادامه یک generated column را برای جدول productsMetadata تعیین خواهیم کرد: 
ALTER TABLE productMetadata
ADD COLUMN id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(data, '$.id'))) STORED NOT NULL

بنابراین زمانیکه یک مقدار JSON را ذخیره میکنیم، کلید اصلی از path تعیین شده استخراج شده و به عنوان یک computed column برای این جدول تعیین خواهد شد. در ادامه میتوانید جزئیات تغییر فوق را مشاهده کنید: 

  
اکنون کوئری زیر را در نظر بگیرید که رکوردی با آی‌دی ۱ را بازیابی خواهد کرد:
SELECT data ->> "$.description.shortDescription" FROM productMetadata
WHERE id = 1;
از آنجائیکه هیچ ایندکسی برای این فیلد جدید لحاظ نشده است، MySQL کل ردیف‌ها را برای یافتن id موردنظر جستجو خواهد کرد. این مورد را میتوانید با دستور EXPLAIN نیز مشاهده کنید:


همانطور که مشاهده میکنید مقدار type به ALL تنظیم شده‌است؛ همچنین مقدار rows نیز تعداد ردیف‌های جدول است که در اینجا ۱۳ ردیف دیتا را داریم. قاعدتاً با اضافه شدن دیتای جدید به جدول، جستجو نیز به مراتب کندتر خواهد شد. بنابراین با اضافه کردن ایندکس میتوانیم مشکل این کند بودن را رفع کنیم. به همین جهت در ادامه یک ایندکس را براساس ستون id که یک generated column است ایجاد خواهیم کرد:

CREATE INDEX idx_json_data ON productMetadata (id);

اکنون اگر یکبار دیگر کوئری قبلی را اجرا کنیم، خواهیم دید که تعداد rows به ۱ و همچنین type به ref ست شده‌اند:



مطالب
تهیه‌ی گزارش از آخرین ارائه‌های یک پروژه در GitHub
چندی قبل مطلب «اطلاع از بروز رسانی نرم افزار ساخته شده» را در سایت جاری مطالعه کردید. در این روش بسیار متداول، شماره نگارش‌های جدید برنامه در یک فایل XML و مانند آن قرار می‌گیرند و برنامه هربار این فایل را جهت یافتن شماره‌های مندرج در آن اسکن می‌کند. اگر پروژه‌ی شما سورس باز است و در GitHub هاست شده، روش دیگری نیز برای یافتن این اطلاعات وجود دارد. در GitHub می‌توان از طریق آدرسی به شکل https://api.github.com/repos/user_name/project_name/releases به اطلاعات آخرین ارائه‌های یک پروژه (قرار گرفته در برگه‌ی releases آن) با فرمت JSON دسترسی یافت (یک مثال). در ادامه قصد داریم روش استفاده‌ی از آن‌را بررسی کنیم.


ساختار JSON ارائه‌های یک پروژه در GitHub

ساختار کلی اطلاعات ارائه‌های یک پروژه در GitHub چنین شکلی را دارد:
[
  {
    release_info,
    "assets": [
      {
      }
    ]
  }
]
در اینجا آرایه‌ای از اطلاعات ارائه‌ی یک پروژه ارسال می‌شود. هر ارائه نیز دارای دو قسمت است: لینکی به صفحه‌ی اصلی release در GitHub و سپس آرایه‌ای به نام assets که در آن اطلاعات فایل‌های پیوستی مانند نام فایل، آدرس، اندازه و امثال آن قرار گرفته‌اند.



تهیه‌ی کلاس‌های معادل فرمت JSON ارائه‌های برنامه در GitHub

اگر بخواهیم قسمت‌های مهم خروجی JSON فوق را تبدیل به کلاس‌های معادل دات نتی کنیم، به دو کلاس ذیل خواهیم رسید:
using Newtonsoft.Json;
using System;

namespace ApplicationAnnouncements
{
    public class GitHubProjectRelease
    {
        [JsonProperty(PropertyName = "url")]
        public string Url { get; set; }

        [JsonProperty(PropertyName = "assets_url")]
        public string AssetsUrl { get; set; }

        [JsonProperty(PropertyName = "upload_url")]
        public string UploadUrl { get; set; }

        [JsonProperty(PropertyName = "html_url")]
        public string HtmlUrl { get; set; }

        [JsonProperty(PropertyName = "id")]
        public int Id { get; set; }

        [JsonProperty(PropertyName = "tag_name")]
        public string TagName { get; set; }

        [JsonProperty(PropertyName = "target_commitish")]
        public string TargetCommitish { get; set; }

        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        [JsonProperty(PropertyName = "body")]
        public string Body { get; set; }

        [JsonProperty(PropertyName = "draft")]
        public bool Draft { get; set; }

        [JsonProperty(PropertyName = "prerelease")]
        public bool PreRelease { get; set; }

        [JsonProperty(PropertyName = "created_at")]
        public DateTime CreatedAt { get; set; }

        [JsonProperty(PropertyName = "published_at")]
        public DateTime PublishedAt { get; set; }

        [JsonProperty(PropertyName = "assets")]
        public Asset[] Assets { get; set; }
    }

    public class Asset
    {
        [JsonProperty(PropertyName = "url")]
        public string Url { get; set; }

        [JsonProperty(PropertyName = "id")]
        public int Id { get; set; }

        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        [JsonProperty(PropertyName = "label")]
        public string Label { get; set; }

        [JsonProperty(PropertyName = "content_type")]
        public string ContentType { get; set; }

        [JsonProperty(PropertyName = "state")]
        public string State { get; set; }

        [JsonProperty(PropertyName = "size")]
        public int Size { get; set; }

        [JsonProperty(PropertyName = "download_count")]
        public int DownloadCount { get; set; }

        [JsonProperty(PropertyName = "created_at")]
        public DateTime CreatedAt { get; set; }

        [JsonProperty(PropertyName = "updated_at")]
        public DateTime UpdatedAt { get; set; }
    }
}
در اینجا از ویژگی JsonProperty جهت معرفی نام‌های واقعی خواص ارائه شده‌ی توسط GitHub استفاده کرده‌ایم.
پس از تشکیل این کلاس‌ها، مرحله‌ی بعد، دریافت اطلاعات JSON از آدرس API ارائه‌های پروژه در GitHub و سپس نگاشت آن‌ها می‌باشد:
using (var webClient = new WebClient())
{
    webClient.Headers.Add("user-agent", "DNTProfiler");
    var jsonData = webClient.DownloadString(url);
    var gitHubProjectReleases = JsonConvert.DeserializeObject<GitHubProjectRelease[]>(jsonData);

    foreach (var release in gitHubProjectReleases)
    {
       foreach (var asset in release.Assets)
       {
           // …
       }
    }
}
حین کار با WebClient یا هر روش دیگری که برای دریافت اطلاعات از وب استفاده می‌کنید، حتما نیاز است user-agent را ذکر کرد. در غیر اینصورت GitHub درخواست شما را برگشت خواهد زد. پس از دریافت اطلاعات JSON، با استفاده از متد JsonConvert.DeserializeObject کتابخانه‌ی JSON.NET، می‌توان آن‌ها را تبدیل به آرایه‌ای از GitHubProjectRelease کرد.

یک نکته: اگر به صفحه‌ی اصلی ارائه‌های یک پروژه در GitHub دقت کنید، شماره‌ی تعداد بار دریافت یک ارائه مشخص نشده‌است. در این API، عدد DownloadCount، بیانگر تعداد بار دریافت پروژه‌ی شما است.
اشتراک‌ها
آموزش Asp.Net Core Web API CRUD با Angular 16

Asp.Net Core Web API CRUD with Angular 16
In this .Net 7 tutorial, we have implemented CRUD operations in asp.net core web api with angular 16 using entity framework core and SQL server. 

آموزش Asp.Net Core Web API CRUD با Angular 16
مطالب
عدم نمایش Ribbon برای کاربران ناشناس (Anonymous Users) در شیرپوینت
یکی از نیاز‌های مشتریان هنگام استفاده از سایت‌های تحت شیرپوینت ، عدم نمایش نوار مدیریتی بالای صفحه یا همان Ribbon برای کاربران ناشناس است .
شاید بتوان گفت که مزیت این راهکار نسبت به دیگر راه کار‌ها ، تصحیح نمایش Scroll Bar مرورگر است که ممکن است در برخی روش‌ها با مشکل مواجه شود. دلیل این امر هم این است که شیرپوینت برای افزودن Ribbon به بالای صفحه Vertical Scroll Bar را از صفحه حذف می‌کند و سپس Scroll Bar سفارشی خود را به صفحه طوری اضافه می‌کند تا نوار Ribbon همیشه در بالا‌ترین نقطه از صفحه بماند
همچنین در این روش از بارگذاری نوار هنگام بالا آمدن سایت نیز البته برای کاربران ناشناس جلوگیری می‌شود
 و اما روش :
 سایت خود را با SharePoint Designer باز کرده و از Master Page یک کپی تهیه کنید و آن را به عنوان پیش فرض سایت تعیین کنید 

سپس تگ زیر را در صفحه بیابید : 
<div id="s4-ribbonrow">
و تگ زیر را به ابتدای آن (قبل از تگ) اضافه کنید : 
<Sharepoint:SPSecurityTrimmedControl runat="server" Permissions="AddDelPrivateWebParts">
مانند تصویر زیر : 

و تگ پایانی آن : 

نکته مهم در استفاده از این تگ ، ویژگی Permissions آن است که باید با دقت و بسته به نیاز شما تعریف شود :

برخی از این موارد عبارتند از : 

EmptyMask – Has no permissions on the Web site. Not available through the user interface.

ViewListItems – View items in lists, documents in document libraries, and view Web discussion comments.

AddListItems – Add items to lists, add documents to document libraries, and add Web discussion comments.

EditListItems – Edit items in lists, edit documents in document libraries, edit Web discussion comments in documents, and customize Web Part Pages in document libraries.

DeleteListItems – Delete items from a list, documents from a document library, and Web discussion comments in documents.

ApproveItems – Approve a minor version of a list item or document.

OpenItems – View the source of documents with server-side file handlers.

ViewVersions – View past versions of a list item or document.

DeleteVersions – Delete past versions of a list item or document.

CancelCheckout – Discard or check in a document which is checked out to another user.

ManagePersonalViews – Create, change, and delete personal views of lists.

ManageLists – Create and delete lists, add or remove columns in a list, and add or remove public views of a list.

ViewFormPages – View forms, views, and application pages, and enumerate lists.

Open – Allow users to open a Web site, list, or folder to access items inside that container.

ViewPages – View pages in a Web site.

AddAndCustomizePages – Add, change, or delete HTML pages or Web Part Pages, and edit the Web site using a SharePoint Foundation–compatible editor.

ApplyThemeAndBorder – Apply a theme or borders to the entire Web site.

ApplyStyleSheets – Apply a style sheet (.css file) to the Web site.

ViewUsageData – View reports on Web site usage.

CreateSSCSite – Create a Web site using Self-Service Site Creation.

ManageSubwebs – Create subsites such as team sites, Meeting Workspace sites, and Document Workspace sites.

CreateGroups – Create a group of users that can be used anywhere within the site collection.

ManagePermissions – Create and change permission levels on the Web site and assign permissions to users and groups.

BrowseDirectories – Enumerate files and folders in a Web site using Microsoft Office SharePoint Designer 2007 and WebDAV interfaces.

BrowseUserInfo – View information about users of the Web site.

AddDelPrivateWebParts – Add or remove personal Web Parts on a Web Part Page.

UpdatePersonalWebParts – Update Web Parts to display personalized information.

ManageWeb – Grant the ability to perform all administration tasks for the Web site as well as manage content. Activate, deactivate, or edit properties of Web site scoped Features through the object model or through the user interface (UI). When granted on the root Web site of a site collection, activate, deactivate, or edit properties of site collection scoped Features through the object model. To browse to the Site Collection Features page and activate or deactivate site collection scoped Features through the UI, you must be a site collection administrator.

UseClientIntegration – Use features that launch client applications; otherwise, users must work on documents locally and upload changes.

UseRemoteAPIs – Use SOAP, WebDAV, or Microsoft Office SharePoint Designer 2007 interfaces to access the Web site.

ManageAlerts – Manage alerts for all users of the Web site.

CreateAlerts – Create e-mail alerts.

EditMyUserInfo – Allows a user to change his or her user information, such as adding a picture.

EnumeratePermissions – Enumerate permissions on the Web site, list, folder, document, or list item.

FullMask – Has all permissions on the Web site. Not available through the user interface.

حال خارج از تگ‌های SPSecurityTrimmedControl در ابتدا یا انتها ، باید تگ login را مانند زیر به آن اضافه کرد . 

و تمام : 

موفق باشید 

نظرات مطالب
پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش سوم (پایانی)
در پاسخ به سوال شما باید عرض کنم که حرف شما کاملا درست است. به این دلیل که با هر فراخوانی از سمت کلاینت لیست permissionهای کاربر واکشی میشود. برای کم کردن این فراخوانی‌ها و واکشی‌ها ما چند راه در پیش رو خواهیم داشت.
1. استفاده از  caching اولین راه حل و ساده‌ترین راه حل هست. به این صورت که در هنگامی که کاربر وارد برنامه میشود تمامی دسترسی‌های او در cache ذخیره شود. برای این کار نیاز به نوشتن سرویسی برای مدیریت این حافظه است. فرض کنید در این حین دسترسی نقشی که کاربر در آن نقش است تغییر کند. بنابراین باید تمامی cache از نو ایجاد شود. توجه کنید که ما نمیتوانیم در معماری REST و Web API از Session استفاده کنیم. به این دلیل که فرایندها در این معماری Stateless هستند، به این معنی که چیزی را در خود نگه نمیدارند.
2. روش دیگر استفاده از Claim در Asp.Net Identity 2 است. در سیستم Identity جدید Asp شما قادر هستید چندین Claim برای کاربر تنظیم (set) کنید. برای این کار میتوانید مقاله ای در این زمینه را از سایت bitOfTech مطالعه کنید. در این مجموعه  مقالات تشریح شده است که چگونه میتوان Claim‌های شخصی سازی شده در سیستم تعریف کرد. در نهایت این Claimها hash شده و در Token کاربر ذخیره میشوند. توصیه میکنم اگر تعداد دسترسی‌های شما در هر نقش زیاد نیست میتوانید از این روش استفاده کنید. البته این را مد نظر داشته باشید در این مقالات نحوه Authorize کردن کاربر با آنچه ما در این مجموعه مقالات تشریح کردیم کمی متفاوت است. 
مطالب
استفاده از SQL-CE به کمک NHibernate

خلاصه‌ای را در مورد SQL Server CE قبلا در این سایت مطالعه‌ کرده‌اید. در ادامه خلاصه‌ای کاربردی را از تنظیمات و نکات مرتبط به کار با SQL-CE به کمک NHibernate ملاحظه خواهید نمود:

1) دریافت SQL-CE 4.0


همین مقدار برای استفاده از SQL-CE 4.0 به کمک NHibernate کفایت می‌کند و حتی نیازی به نصب سرویس پک یک VS 2010 هم نیست.

2) ابزار سازی جهت ایجاد یک بانک اطلاعاتی خالی SQL-CE

using System;
using System.IO;

namespace NHibernate.Helper.DbSpecific
{
public class SqlCEDbHelper
{
const string engineTypeName = "System.Data.SqlServerCe.SqlCeEngine, System.Data.SqlServerCe";

/// <summary>
/// note: this method will delete existing db and then creates a new one.
/// </summary>
/// <param name="filename"></param>
/// <param name="password"></param>
public static void CreateEmptyDatabaseFile(string filename, string password = "")
{
if (File.Exists(filename))
File.Delete(filename);

var type = System.Type.GetType(engineTypeName);
var localConnectionString = type.GetProperty("LocalConnectionString");
var createDatabase = type.GetMethod("CreateDatabase");

var engine = Activator.CreateInstance(type);

string connectionStr = string.Format("Data Source='{0}';Password={1};Encrypt Database=True", filename, password);
if (string.IsNullOrWhiteSpace(password))
connectionStr = string.Format("Data Source='{0}'", filename);

localConnectionString.SetValue(
obj: engine,
value: connectionStr,
index: null);
createDatabase.Invoke(engine, new object[0]);
}

/// <summary>
/// use this method to compact or encrypt existing db or decrypt it to a new db with all records
/// </summary>
/// <param name="sourceConnection"></param>
/// <param name="destConnection"></param>
public static void CompactDatabase(string sourceConnection, string destConnection)
{
var type = System.Type.GetType(engineTypeName);
var engine = Activator.CreateInstance(type);

var localConnectionString = type.GetProperty("LocalConnectionString");
localConnectionString.SetValue(
obj: engine,
value: sourceConnection,
index: null);

var compactDatabase = type.GetMethod("Compact");
compactDatabase.Invoke(engine, new object[] { destConnection });
}
}
}

کلاس فوق، یک کلاس عمومی است و مرتبط به NHibernate نیست و در همه جا قابل استفاده است.
متد CreateEmptyDatabaseFile یک فایل بانک اطلاعاتی خالی با فرمت مخصوص SQL-CE را برای شما تولید خواهد کرد. به این ترتیب می‌توان بدون نیاز به ابزار خاصی، سریعا یک بانک خالی را تولید و شروع به کار کرد. در این متد اگر کلمه عبوری را وارد نکنید، بانک اطلاعاتی رمزنگاری شده نخواهد بود و اگر کلمه عبور را وارد کنید، دیتابیس اولیه به همراه کلیه اعمال انجام شده بر روی آن در طول زمان، با کمک الگوریتم AES به صورت خودکار رمزنگاری خواهند شد. کل کاری را هم که باید انجام دهید ذکر این کلمه عبور در کانکشن استرینگ است.
متد CompactDatabase، یک متد چند منظوره است. اگر بانک اطلاعاتی SQL-CE رمزنگاری نشده‌ای دارید و می‌خواهید کل آن‌را به همراه تمام اطلاعات درون آن رمزنگاری کنید، می‌توانید جهت سهولت کار از این متد استفاده نمائید. آرگومان اول آن به کانکشن استرینگ بانکی موجود و آرگومان دوم به کانکشن استرینگ بانک جدیدی که تولید خواهد شد، اشاره می‌کند.
همچنین اگر یک بانک اطلاعاتی SQL-CE رمزنگاری شده دارید و می‌خواهید آن‌را به صورت یک بانک اطلاعاتی جدید به همراه تمام رکوردهای آن رمزگشایی کنید، باز هم می‌توان از این متد استفاده کرد. البته بدیهی است که کلمه عبور را باید داشته باشید و این کلمه عبور جایی درون فایل بانک اطلاعاتی ذخیره نمی‌شود. در این حالت در کانکشن استرینگ اول باید کلمه عبور ذکر شود و کانکشن استرینگ دوم نیازی به کلمه عبور نخواهد داشت.

فرمت کلی کانکشن استرینگ SQL-CE هم به شکل زیر است:

Data Source=c:\path\db.sdf;Password=1234;Encrypt Database=True

البته این برای حالتی است که قصد داشته باشید بانک اطلاعاتی مورد استفاده را رمزنگاری کنید یا از یک بانک اطلاعاتی رمزنگاری شده استفاده نمائید. اگر بانک اطلاعاتی شما کلمه عبوری ندارد، ذکر Data Source=c:\path\db.sdf کفایت می‌کند.

این کلاس هم از این جهت مطرح شد که NHibernate می‌تواند ساختار بانک اطلاعاتی را بر اساس تعاریف نگاشت‌ها به صورت خودکار تولید و اعمال کند، «اما» بر روی یک بانک اطلاعاتی خالی SQL-CE از قبل تهیه شده (در غیراینصورت خطای The database file cannot be found. Check the path to the database را دریافت خواهید کرد).

نکته:
اگر دقت کرده باشید در این کلاس engineTypeName به صورت رشته ذکر شده است. چرا؟
علت این است که با ذکر engineTypeName به صورت رشته، می‌توان از این کلاس در یک کتابخانه عمومی هم استفاده کرد، بدون اینکه مصرف کننده نیازی داشته باشد تا ارجاع مستقیمی را به اسمبلی SQL-CE به برنامه خود اضافه کند. اگر این ارجاع وجود داشت، متدهای یاد شده کار می‌کنند، در غیراینصورت در گوشه‌ای ساکت و بدون دردسر و بدون نیاز به اسمبلی خاصی برای روز مبادا قرار خواهند گرفت.


3) ابزار مرور اطلاعات بانک اطلاعاتی SQL-CE

با استفاده از management studio خود SQL Server هم می‌شود با بانک‌های اطلاعاتی SQL-CE کار کرد، اما ... اینبار برخلاف نگارش کامل اس کیوال سرور، با یک نسخه‌ی بسیار بدوی، که حتی امکان rename فیلدها را هم ندارد مواجه خواهید شد. به همین جهت به شخصه برنامه SqlCe40Toolbox را ترجیح می‌دهم و اطمینان داشته باشید که امکانات آن برای کار با SQL-CE از امکانات ارائه شده توسط management studio مایکروسافت، بیشتر و پیشرفته‌تر است!



4) تنظیمات NHibernate جهت کار با SQL-CE

الف) پس از نصب SQL-CE ، فایل‌های آن‌را در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0 می‌توان یافت. درایور ADO.NET آن هم در مسیر C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop قرار دارد. بنابراین در ابتدا نیاز است تا ارجاعی را به اسمبلی System.Data.SqlServerCe.dll به برنامه خود اضافه کنید (نام پوشه desktop آن هم غلط انداز است. از این جهت که نگارش 4 آن، به راحتی در برنامه‌های ذاتا چند ریسمانی ASP.Net بدون مشکل قابل استفاده است).
نکته مهم: در این حالت NHibernate قادر به یافتن فایل درایور یاد شده نخواهد بود و پیغام خطای «Could not create the driver from NHibernate.Driver.SqlServerCeDriver» را دریافت خواهید کرد. برای رفع آن، اسمبلی System.Data.SqlServerCe.dll را در لیست ارجاعات برنامه یافته و در برگه خواص آن، خاصیت «Copy Local» را true کنید. به این معنا که NHibernate این اسمبلی را در کنار فایل اجرایی برنامه شما جستجو خواهد کرد.

ب) مطلب بعد، تنظیمات ابتدایی NHibernate‌ است جهت شناساندن SQL-CE . مابقی مسایل (نکات mapping، کوئری‌ها و غیره) هیچ تفاوتی با سایر بانک‌های اطلاعاتی نخواهد داشت و یکی است. به این معنا که اگر برنامه شما از ویژگی‌های خاص بانک‌های اطلاعاتی استفاده نکند (مثلا اگر از رویه‌های ذخیره شده اس کیوال سرور استفاده نکرده باشد)، فقط با تغییر کانکشن استرینگ و معرفی dialect و driver جدید، به سادگی می‌تواند به یک بانک اطلاعاتی دیگر سوئیچ کند؛ بدون اینکه حتی بخواهید یک سطر از کدهای اصلی برنامه خود را تغییر دهید.



تنها نکته جدید آن این متد است:

private Configuration getConfig()
{
var configure = new Configuration();
configure.SessionFactoryName("BuildIt");

configure.DataBaseIntegration(db =>
{
db.ConnectionProvider<DriverConnectionProvider>();
db.Dialect<MsSqlCe40Dialect>();
db.Driver<SqlServerCeDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.IsolationLevel = IsolationLevel.ReadCommitted;
db.ConnectionString = ConnectionString;
db.Timeout = 10;

//for testing ...
db.LogFormattedSql = true;
db.LogSqlInConsole = true;
});

return configure;
}

که در آن نحوه تعریف MsSqlCe40Dialect و SqlServerCeDriver مشخص شده است.

نکته حاشیه‌ای!
در این مثال primary key از نوع identity تعریف شده و بدون مشکل کار کرد. همین را اگر با EF تست کنید، این خطا را دریافت می‌کنید: «Server-generated keys and server-generated values are not supported by SQL Server Compact». بله، EF نمی‌تواند با primary key از نوع identity حین کار با SQL-CE کار کند. برای رفع آن توصیه شده است که از Guid استفاده کنید!

نکته تکمیلی:
استفاده از Dialect سفارشی در NHibernate


نکته پایانی!
و در پایان باید اشاره کرد که SQL-CE یک بانک اطلاعاتی نوشته شده با دات نت نیست (با CPP نوشته شده است و نصب آن هم نیاز به ران تایم به روز VC را دارد). به این معنا که جهت سیستم‌های 64 بیتی و 32 بیتی باید نسخه مناسب آن‌را توزیع کنید. یا اینکه Target platform پروژه جاری دات نت خود را بر روی X86 قرار دهید (نه بر روی Any CPU پیش فرض) و در این حالت تنها یک نسخه X86 بانک اطلاعاتی SQL-CE و همچنین برنامه خود را برای تمام سیستم‌ها توزیع کنید.

مطالب
تعیین شمای جداول بانکی در EF Code First
کلاس‌های زیر را در نظر بگیرید:
public class Customer
{
   public virtual string Id { get; set; }
   public virtual string Name { get; set; }
   public virtual DateTime HireDate { get; set; }
}
و Context ی که کلاس فوق به آن اضافه میشود:
public class MyContext : DbContext
{
    public DbSet<Customer> Customers{ set; get; }
}
حالا زمان ایجاد جدول، با توجه به دسترسی کاربر بانک داده، جداول ایجاد میشوند:
CREATE TABLE Customers (
    [Id] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    [HireDate] datetime,
    CONSTRAINT [PK_Product] PRIMARY KEY ([Id])
)
ولی به محض ارسال درخواست و عدم دسترسی و نیز اقدام به ایجاد دوباره جداول در صورت نبود آن، به مشکل زیر بر میخوریم:
Invalid object name 'dbo.Customers'.
lشکال در شمای پیش فرض Entityframework است که در زمان درخواست بصورت پیش فرض از dbo استفاده میکند، برای حل مشکل در هنکام نگاشت دو راه موجود است:
راه حل اول(مزین کردن کلاس):
[Table("Customers", Schema = "dbSchemaUser")]
public class Customer
{
    ...
راه حل دوم(استفاده از نگاشت کننده در context):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>().ToTable(tableName: "Customers", schemaName: "dbSchemaUser");
   ...
که نتیجه کار بصورت عبارات sql زیر می‌باشد:
CREATE TABLE dbSchemaUser.Customers (
    [Id] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    [HireDate] datetime,
    CONSTRAINT [PK_Product] PRIMARY KEY ([Id])
)




مطالب
ارسال ایمیل توسط PowerShell
امروز می‌خوام یکی از کاربرد‌های جالب PowerShell  رو براتون بگم. PowerShell و اجراش کنید.
- کد زیر را ابتدا ویرایش کرده و نام کاربری و رمز عبور خود را در آن وارد نمایید.(من در اینجا از سیستم Gmail برای ارسال ایمیل استفاده کرده ام)
- متن دلخواه خود را در بخش $body قرار دهید.
- در نهایت متن خود را کپی کرده و با کلیک راست بر روی PowerShell متن کپی شده در انجا قرار میگیرد.
- حالا کلید Enter را فشار دهید.
ایمیل در صورتی که اطلاعات درست وارد شده باشد به ایمیل مقصد ارسال گردیده است.  :)

    $EmailFrom = "test@gmail.com"

    $EmailTo = "info@dotnettips.info"

    $Subject = "The subject of your email"

    $Body = "What do you want your email to say"

    $SMTPServer = "smtp.gmail.com"

    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)

    $SMTPClient.EnableSsl = $true

    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("test", "123456");

    $SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)