آمار بازدید و استفاده کاربران از اسناد و صفحات در شیرپوینت

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

پیگیری تعاملات کاربران با اسناد و صفحات می‌تواند به شما کمک کند:

  • اسناد و صفحات پرکاربرد و پربازدید را شناسایی کنید.
  • محتوا را بهینه کنید و تعامل کاربران را بهبود بدهید.

مرحله ۱: یافتن محل ذخیره‌سازی آمار بازدید در دیتابیس

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

  • Central Administration را باز کنید.
  • از منوی سمت چپ روی Monitoring کلیک کنید.
  • وارد Configure usage and health data collection شوید.
  • در بخش Logging Database Server سرور دیتابیس و نام دیتابیس را مشاهده کنید.
  • این اطلاعات را جایی یادداشت کنید.

معمولاً نام دیتابیس چیزی شبیه WSS_UsageApplication است.

با استفاده از نرم‌افزار  SQL Server Management Studioیا SSMS دیتابیسی که یادداشت کرده‌اید را باز کنید و بررسی کنید که آیا در آن جداولی مانند RequestUsage_Partition وجود دارد؟ این جداول چون معمولاً شامل لاگ‌های حجیم هستند، داده‌ها را به شکل پارتیشن‌بندی‌شده بر اساس بازه‌های زمانی ذخیره می‌کنند تا از نظر پرفورمنس بهینه‌تر باشند.

مثلاً اگر قرار است فقط آمار بازدید یک هفته ذخیره شوند، اطلاعات از روز اول تا روز هفتم به صورت جداگانه در پارتیشن های 01 تا 07 ذخیره می‌شوند و در روز هشتم، پارتیشن 01 خالی می‌شود و اطلاعات روز هشتم در آن قرار می‌گیرد.

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

مرحله ۲: ساختار کوئری

کوئری SQL زیر اطلاعات مربوط به تاریخ بازدید، جزئیات سایت، مسیر اسناد و نام کاربری بازدیدکنندگان را از میان چندین پارتیشن دیتابیس استخراج می‌کند؛ در این کوئری برای مشخص کردن صفحات بازدید شده چک می‌کنیم که ستون DocumentPath شامل عبارت aspx باشد و برای مشخص کردن اسناد بازدید شده از چک می‌کنیم که ستون DocumentPath شامل عبارت‌های pdf و docx و xslx و pptx باشد؛ این چهار عبارت به ترتیب مرتبط با اسناد پی‌دی‌اف، وُرد، اکسل و پاورپوینت هستند؛ اگر نیاز به اسناد با فرمت‌های دیگری داشته باشید، باید فرمت‌های مورد نظر خود را به کوئری خود اضافه کنید.

به غیر از فیلتر کردن داده‌ها بر اساس فرمت، در کوئری فقط لاگ‌هایی که فیلد نام کاربری آن‌ها خالی نیست، انتخاب می‌شوند. ستون‌هایی که در کوئری استفاده کرده‌ایم بدین شرح هستند: VisitDate برای تاریخ بازدید، SiteId و SiteUrl برای سایت بازدید شده، DocumentPath برای مسیر فایل یا صفحه بازدید شده، Title برای عنوان سند یا صفحه و در نهایت UserLogin برای دریافت حساب کاربری بازدیدکننده:

DECLARE @sql NVARCHAR(MAX) = 'WITH CombinedData AS (';

DECLARE @i INT = 0;
DECLARE @end INT = 31;
WHILE @i <= @end

BEGIN
    SET @sql = @sql + '
    SELECT
        CAST(LogTime AS DATE) AS VisitDate,
        SiteId,
        SiteUrl,
        DocumentPath,
        Title,
        UserLogin
    FROM [DatabaseName].[dbo].[RequestUsage_Partition' + CAST(@i AS NVARCHAR) + ']
    WHERE
        (
            DocumentPath LIKE ''%.aspx%'' OR
            DocumentPath LIKE ''%.pdf%'' OR
            DocumentPath LIKE ''%.docx%'' OR
            DocumentPath LIKE ''%.xlsx%'' OR
            DocumentPath LIKE ''%.pptx%''
        )
    AND UserLogin IS NOT NULL';
    IF @i < @end SET @sql = @sql + ' UNION ALL ';
    SET @i = @i + 1;
END

SET @sql = @sql + ')

SELECT
    VisitDate,
    SiteId,
    SiteUrl,
    DocumentPath,
    Title,
    UserLogin
FROM CombinedData
ORDER BY VisitDate, UserLogin;';

EXEC sp_executesql @sql;

این اسکریپت از یک لوپ WHILE استفاده می‌کند تا به صورت داینامیک، اطلاعات بازدید کاربران را از پارتیشن‌ها دریافت کند؛ استفاده از چنین کوئری‌هایی در سیستم‌هایی که اطلاعات آن‌ها به صورت پارتیشنی ذخیره می‌شوند رایج است؛ می‌توانید از این کوئری در SSMS استفاده کنید و مطمئن شوید که کوئری درست کار می‌کند.

مرحله ۳: وارد کردن نتایج در پاور بی‌آی

پس از این که از نتایج کوئری مطمئن شدید، نرم‌افزار پاور بی‌آی دسکتاپ را باز کرده و از طریق Get data و سپس SQL Server به دیتابیس خود متصل شوید. برای دریافت اطلاعات از دیتابیس باید در پنجره‌ای که باز می‌شود، نام سرور، نام دیتابیس و اسکریپت SQL را وارد کنید.

دریافت آمار بازدید کاربران و استفاده از این داده ها در شیرپوینت

مرحله ۴: طراحی دشبورد

از Power Query Editor برای تمیز کردن داده‌ها و آماده‌سازی آن‌ها استفاده کنید و از این طریق داده‌ها را برای ایجاد یک دشبورد تعاملی آماده کنید؛ من برای این پرژه، ابتدا تاریخ بازدیدها را شمسی کردم و اسم داکیومنت‌ها و صفحات را از ستون DocumentPath استخراج کردم تا خواناتر باشند. پس از آماده شدن داده‌ها، Close & Apply را بزنید تا به محیط طراحی دشبورد بازگردید.

همان طور که در تصویر زیر می‌بینید از پنج ویژوال برای نمایش داده‌ها استفاده کرده‌ام:

  • تعداد بازدید به تفکیک روز
  • سایت‌های پُر بازدید
  • اسناد پُر بازدید
  • صفحات پُر بازدید
  • کاربران بر اساس تعداد بازدید

مرکز اسناد در شیرپوینت: 5 مرحله برای پیاده سازی مرکز اسناد با Content Search Web Part

Content Search Web Part چیست؟

Content Search Web Part یا به اختصار CSWP، یک وب پارت است که از قابلیت‌های جستجوی شیرپوینت برای فراخوانی محتوا و نمایش آن در قالبی خاص استفاده می‌کند. برخلاف Content Query Web Part که داده‌ها را به‌صورت مستقیم از لیست‌ها یا کتابخانه‌ها فراخوانی می‌کند، CSWP بر اساس محتوای ایندکس‌شده عمل می‌کند و برای مجموعه داده‌های خیلی بزرگ عملکرد بهتری دارد.

مزایای ایجاد پورتال مرکز اسناد

ایجاد مرکز اسناد در شیرپوینت این امکان را برای کاربران فراهم می‌کند تا سریع‌تر به اسناد و محتوای مورد نیاز خود دسترسی پیدا کنند؛ این راهنما شامل پنج مرحله برای ایجاد وب پارت زون‌ها در قالب صفحه، تنظیم وب پارت جستجوی محتوا (Content Search Web Part) و سفارشی‌سازی قالب‌های نمایش (Display Templates) برای به وجود آوردن تجربه‌ای کارآمد برای کاربران سیستم مرکز اسناد در شیرپوینت سازمان است.

یک پورتال مرکز اسناد در شیرپوینت می‌تواند:

  • مدیریت و دسترسی کاربران به اسناد را آسان کند کند.
  • امکان جستجو و دسترسی سریع‌تر به اسناد مربوط با کاربر را فراهم نماید.
  • با طراحی سفارشی تجربه استفاده کاربر از مرکز اسناد در شیرپوینت را بهبود بخشد.

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

مرحله ۱: قرار دادن وب پارت زون‌ها در قالب صفحه

در این پروژه، به چهار وب پارت زون برای چهار وب پارت نیاز داریم:

  • اولین وب پارت: نمایش اسنادی که اخیراً تغییر کرده‌اند.
  • دومین وب پارت: نمایش همه اسناد به ترتیب زمان بارگزاری سند.
  • سومین وب پارت: فیلترهای مرتبط با فرمت فایل و شخص سازنده فایل.
  • چهارمین وب پارت: جستجو در مرکز اسناد در شیرپوینت.

برای قرار دادن وب پارت زون در قالب صفحه، ابتدا باید فایل html قالب صفحه را در ویرایشگر کد باز کنید و بعد به بخش مدیریت طراحی شیرپوینت بروید. از منوی مدیریت طراحی روی گزینه ششم که شامل قالب‌های صفحه است کلیک کنید. روی عبارت Conversion Successful روبروی اسم قالب صفحه کلیک کنید (به فارسی، تبدیل موفقیت‌آمیز ترجمه شده است) و در صفحه جدید از نوار بالا روی Snippets کلیک کنید تا صفحه Snipptes باز شود.

مقدمات ایجاد مرکز اسناد در شیرپوینت

در صفحه Snippets از منوی بالا روی Web Part Zone کلیک کنید تا یک کد تولید شود. کد تولید شده را کپی کنید و در قالب html خود، هر جا که نیاز به اضافه کردن وب پارت دارید پیست کنید. همین کار را سه بار دیگر انجام دهید تا برای هر چهار وب پارتی که در این پروژه نیاز داریم، وب پارت زون داشته باشیم. دقت کنید که برای اضافه کردن هر وب پارت زون، حتماً ابتدا یک بار روی دکمه Web Part Zone کلیک کنید تا یک کد جدید ایجاد شود.

در نهایت فایل html قالب صفحه را ذخیره کنید، اگر قبلاً قالب صفحه را منتشر و اعمال نکرده‌اید در بخش مدیریت طراحی آن را منتشر کنید و در صفحه مد نظر خود، قالب صفحه را اعمال کنید.

مرحله ۲: اضافه کردن و پیکربندی CSWP

در این بخش، ما به دو وب پارت جستجوی محتوا نیاز داریم: یکی برای نمایش اسنادی که اخیراً تغییر پیدا کرده‌اند و یکی برای همه اسناد. برای اضافه کردن اولین وب پارت مراحل زیر را طی کنید:

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

روی دکمه افزودن وب پارت کلیک کنید و وب پارت جستجوی محتوا را به دو وب پارت زونی که نیاز دارید اضافه کنید. سپس روی علامت کوچکی که گوشه هر وب پارت ظاهر می‌شود کلیک کنید و با کلیک روی «ویرایش وب پارت» وارد تنظیمات وب پارت اول شوید؛ در این وب پارت می‌خواهیم همه اسنادی را ببینیم که اخیراً ویرایش شده‌اند.

روی دکمه Change Query که سمت چپ تصویر ظاهر شده است کلیک کنید تا بتوانید کوئری متناسب را تعریف کنید:

همان طور که در تصویر مشاهده می‌کنید از کوئری زیر استفاده کردم تا همه اسناد نمایش داده شوند:

(FileExtension:doc OR FileExtension:docx OR FileExtension:xls OR FileExtension:xlsx OR FileExtension:ppt OR FileExtension:pptx OR FileExtension:pdf) (IsDocument:"True")

از تب Sorting هم برای نمایش اسناد به ترتیب زمان تغییر استفاده می‌کنیم:

در نهایت، تأیید را می‌زنیم و تعداد موارد جهت نمایش را مشخص می‌کنیم. فعلاً از فیلد مورد یا Item، قالب Two Lines را برای نمایش اسناد انتخاب می‌کنیم و در نهایت، تأیید را می‌زنیم تا تغییرات ذخیره شوند.

برای وب پارت جستجوی دوم هم همین مراحل را طی می‌کنیم، اما در تب Sorting ترتیب موارد را بر اساس فیلد Created قرار می‌دهیم تا این بار جدیدترین موارد بالاتر قرار بگیرند؛ چون قرار است این وب پارت نمایش دهنده نتایج جستجو هم باشد، عبارت SearchBoxQuery هم به متن کوئری اضافه می‌کنیم:

(FileExtension:doc OR FileExtension:docx OR FileExtension:xls OR FileExtension:xlsx OR FileExtension:ppt OR FileExtension:pptx OR FileExtension:pdf) (IsDocument:"True")
{SearchBoxQuery}

سایر تنظیمات کوئری که در این پنجره وجود دارند هم در بسیاری موارد کاربرد دارند؛ مثلاً می‌توانید از یک پارامتر که در URL وجود دارد برای ایجاد کوئری استفاده کنید، یا فقط مواردی که در یک یا چند لیست یا مخزن اسناد وجود دارند را فراخوانی کنید؛ برای این پروژه، ما فقط از همین تنظیماتی که ذکر شد استفاده می‌کنیم.

در نهایت تأیید را بزنید و از تنظیمات وب پارت، در فیلد کنترل، گزینه List With Paging را انتخاب کنید و در فیلد مورد، Two Lines را بزنید. دوباره تأیید را بزنید و صفحه را ذخیره کنید.

مرحله ۳: ایجاد و سفارشی‌سازی قالب‌های نمایش (Display Templates)

قالب‌های نمایش تعیین می‌کنند که نتایج جستجو در وب پارت CSWP با چه ظاهری نمایش داده شوند؛ مثل قالب صفحات، این قالب‌ها هم فایل‌های html هستند که در فولدر Display Templates و سپس در فولدر Content Web Parts قرار دارند و با استفاده از جاوااسکریپت و css شخصی سازی می‌شوند. برای ایجاد یک Display Template جدید باید مراحل زیر را طی کنید:

  • یکی از فایل‌های html موجود در این فولدر، مثلاً فایل Item_TwoLines.html را کپی کنید
  • در همین فولدر پیست کنید و نام فایل جدید را به نام دلخواه خود تغییر دهید
  • فایل جدید را در ویرایشگر کد باز کنید
  • از تگ title برای تغییر نام نمایشی فایل استفاده کنید
  • از تگ ManagedPropertyMapping استفاده کنید تا مشخصات مورد نیاز آیتم را تعریف کنید
  • از بخش html استفاده کنید تا ظاهر قالب را به شکل دلخواه خود طراحی کنید

برای انتخاب قالب ایجاد شده، باید مجدداً وارد حالت ویرایش صفحه شوید و در تنظیمات وب پارت، این بار این قالب را در فیلد مورد (Item) انتخاب کنید. قالبی که در فیلد کنترل (Control) انتخاب می‌کنید، یک فایل html است که به ازای هر وب پارت یک بار در صفحه فراخوانی می‌شود و قالب مورد (Item)، همان طور که از نامش مشخص است، یک قالب html است که به تعداد آیتم‌هایی که وب پارت برمی‌گرداند در صفحه تکرار می‌شود.

مرحله ۴: جستجو

برای ایجاد قابلیت جستجو در مرکز اسناد در شیرپوینت، کافی است دوباره وارد حالت ویرایش صفحه بشوید و وب پارت جعبه جستجو یا Search Box را به یکی از وب پارت زون‌ها اضافه کنید:

سپس وارد تنظیمات وب پارت جستجو شوید و از این که جستجو در وب پارت مد نظر شما انجام می‌پذیرد اطمینان یابید.

مرحله 5: فیلتر

برای اضافه کردن امکان فیلتر کردن نتایج، از وب پارت تصفیه یا همان Refinement استفاده می‌کنیم؛ مثل وب پارت جعبه جستجو، در تنظیمات این وب پارت هم باید مشخص کنید که نتایج کدام وب پارت را فیلتر کند و همچنین انتخاب کنید که فیلتر بر اساس کدام فیلدها عمل کند. برای نمونه من از فیلد FileType برای فیلتر بر اساس فرمت اسناد و از DisplayAuthor برای فیلتر بر اساس نام ایجادکننده فایل استفاده کردم.

در نهایت حتماً عملکرد همه بخش‌های پورتال طراحی شده را تست کنید، دسترسی‌های کاربران مورد نیاز را بررسی کنید و مطمئن شوید همه چیز مطابق برنامه پیش می‌رود؛ با دنبال کردن این مراحل، می‌توانید یک مرکز اسناد در شیرپوینت طراحی کنید که مدیریت اسناد را بهبود بخشد، دسترسی به محتوا را سریع‌تر کند، و تجربه کاربری را ارتقا دهد.

مسترپیج در شیرپوینت: مقدمه‌ای درباره ایجاد مسترپیج و قالب صفحات

مسترپیج در شیرپوینت (Master Page) و قالب صفحه در شیرپوینت (Page Template یا Page Layout)، ابزارهایی قدرتمند برای توسعه‌دهندگان هستند که امکان تنظیم یکپارچه ساختار، برندینگ و ظاهر سایت را فراهم می‌کنند. این عناصر کنترل کاملی بر نحوه نمایش محتوا در سایت ارائه می‌دهند. قبلاً درباره ساختن مسترپیج مطلب مختصری نوشته‌ام. در این راهنما نیز موارد زیر را در قالب مثال‌های عملیاتی بررسی می‌کنیم:

  • ایجاد مسترپیج در شیرپوینت و اعمال آن روی صفحات سایتی
  • ساخت یک قالب صفحه و ایجاد صفحه جدید بر اساس آن قالب
  • استفاده از اسنیپت‌هایی مانند لوگوی سایت و وب‌پارت‌زون (Web Part Zone)

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

قالب صفحه (Page Templates یا Page Layout)، چیدمان‌های از پیش طراحی‌شده‌ای برای ساخت سریع صفحات هستند؛ اسنیپت‌ها هم به شخصی‌سازی سایت کمک می‌کنند و عناصری مانند عنوان سایت، لوگو یا وب‌پارت‌ها را به‌صورت پویا در مسترپیج یا قالب صفحات وارد می‌کنند؛ با تسلط بر این امکانات می‌توان تجربه کاربری و عملکرد سایت‌های شیرپوینت را به‌طور قابل‌توجهی ارتقا داد.

قبل از ایجاد مسترپیج، باید مطمئن شویم که:

  • ویژگی‌های مرتبط با زیرساخت انتشار (Publishing Infrastructure) در سایت فعال شده باشد.
  • کاربری که مسترپیج را ایجاد می‌کند، دسترسی لازم برای استفاده از Design Manager داشته باشد.

مراحل ایجاد مسترپیج در شیرپوینت

برای ساختن یک مسترپیج جدید، باید مراحل زیر را طی کنید:

روی چرخ‌دنده تنظیمات کلیک کنید و وارد Design Manager شوید؛ این مورد در محیط شیرپوینت فارسی ممکن است «مدیریت طراحی» یا «مدیر طراحی» ترجمه شده باشد. سپس از منوی داخل صفحه مدیریت طراحی، گزینه سوم، یعنی آپلود پرونده طراحی را انتخاب کنید.

لینکی که در خط اول صفحه ظاهر شده است، لینک فولدر مرتبط با فایل‌های طراحی مسترپیج شما است؛ این لینک را کپی کنید. سپس یک فولدر در ویندوز باز کنید و از منوی سمت چپ، روی گزینه Network، کلیک‌راست کنید و گزینه Map network drive را بزنید. در پنجره باز شده، لینکی که کپی کردید را پیست کنید و سپس اطلاعات مرتبط به حساب خود را وارد کنید.

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

من معمولاً از نرم‌افزار VS Code برای ویرایش کردن کدهای مرتبط با شیرپوینت استفاده می‌کنم؛ اگر شما هم از VS Code استفاده می‌کنید، می‌توانید پس از باز کردن نرم‌افزار، Open Folder را بزنید و همه فولدری که مپ کرده‌اید را در محیط VS Code باز کنید. بدین طریق می‌توانید به راحتی در همه فایل‌های مرتبط به مسترپیج شیرپوینت خود جستجو کنید و در صورت نیاز فایل‌ها را تغییر دهید و ذخیره کنید.

استفاده از اسنیپت‌های مسترپیج در شیرپوینت

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

  • در صفحه مرتبط با مسترپیج‌ها در مدیریت طراحی، روی لینک تبدیل موفقیت آمیز (Conversion Successful) کلیک کنید
  • در صفحه جدید، روی لینک Snippets در بالای کلیک کنید
  • از تب طراحی بالای صفحه استفاده کنید و روی لوگوی سایت کلیک کنید
  • کد html نمایش داده شده در صفحه را کپی کنید
  • کد کپی شده را در قالب html مسترپیج در هر جایی که می‌خواهید نمایش داده شود پیست کنید
  • فایل را ذخیره کنید

برای دیدن تغییرات می‌توانید دوباره روی لینک تبدیل موفقیت آمیز (در صفحه مرتبط با مسترپیج‌ها) کلیک کنید. می‌بینید که لوگو در بخشی که اسنیپت html را در آن قرار دادید نمایش داده می‌شود. اسنیپت‌های زیادی هستند که می‌توانید در قسمت‌های مختلف مسترپیج و قالب صفحات استفاده کنید. مثلاً در مسترپیج می‌توانید برای نمایش ناوبری (Navigation) و عنوان سایت از اسنیپت‌ها استفاده کنید.

در مسترپیج‌ها می‌توانید از کدهای CSS و اسکریپت‌های JavaScript استفاده کنید و طراحی‌ها و عملکردهای مختلفی را در صفحات پیاده‌سازی کنید. برای هر چه ساده‌تر بودن توسعه صفحات پیشنهاد می‌کنم تا جایی که می‌توانید استایل‌های CSS را در یک فایل CSS نگهداری کنید اما اسکریپت‌ها را در فایل‌های مختلف و کوچک و کم‌حجم ذخیره کنید تا مدیریتشان ساده‌تر باشد.

قالب صفحه (Page Template یا Page Layout) چیست؟

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

مراحل ساخت قالب صفحات

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

استفاده از اسنیپت Web Part Zone در قالب صفحات

در صفحه Design Manager روی گزینه ششم کلیک کنید و Conversion Successful را بزنید. در صفحه جدید روی Snippets کلیک کنید. یکی از اسنیپت‌هایی که در قالب‌های صفحات پُرکاربرد است، اسنیپت Web Part Zone است؛ همان طور که از نام این اسنیپت مشخص است، فضایی را برای قرار دادن تعداد نامحدودی وب‌پارت آماده می‌کند. بدین ترتیب می‌توانید قالب‌هایی طراحی کنید که با این که طراحی مشخصی دارند، می‌توانند شامل انواع وب‌پارت‌ها باشند و امکاناتی نامحدود در اختیار مدیر شیرپوینت برای طراحی صفحه قرار می‌دهند.

جمع‌بندی

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

پاورفرم: 5 قدم برای ساختن قالب پرینت فرم در شیرپوینت

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

توی این پست بررسی می‌کنیم که چطوری می‌شه با استفاده از یه فایل XSLT، نمای مخصوصی برای پرینت فرم پاورفرم طراحی کرد و گام به گام نحوه پیاده‌سازی این قابلیت رو بررسی می‌کنیم؛ برای ایجاد این نمای مخصوص چاپ در فرم‌های پاورفرم، مراحل زیر رو باید طی کنیم:

1. تنظیماتِ فرمِ پاورفرم

فرض من اینه که شما به طراحی فرم با پاورفرم مسلط هستید، یه خُرده HTML و CSS می‌دونید و قبلاً فرم مد نظرتونو توی پاورفرم طراحی کردید. بعد از تکمیل طراحی فرم، باید توی محیط ویرایشگر فرم پاورفرم، وارد بخش Options بشید و توی بخش General Settings، تیک Show Print Button رو فعال کنید:

تنظیمات در پاورفرم

برای این که پاورفرم، فرم رو برای پرینت آماده‌سازی کنه، نیاز به یه فایل با فرمت XSLT داره. فایل‌های XSLT فایل‌هایی هستن که به عنوان قالب (template) برای نمایش اطلاعات XML استفاده می‌شن؛ بنابراین لازمه که یه فایل با فرمت XSLT بسازید، با هر ویرایشگری که راحت هستید بازش کنید و این محتوا رو داخلش قرار بدید:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     <xsl:output method="xml" encoding="utf-8" indent="no"/>
     <xsl:template match="/">
          <table style="border: 2px das">
                <xsl:for-each select="ListItem/Control">
                     <tr>
                          <td>
                              <xsl:value-of select="@ControlName"/>
                         </td>
                         <td>
                              <xsl:value-of select="@ControlType"/>
                         </td>
                         <td>
                              <xsl:value-of select="."/>
                         </td>
                    </tr>
               </xsl:for-each>
          </table>
     </xsl:template>
</xsl:stylesheet>

این فایل XSLT رو ذخیره کنید و یه جایی توی یکی از مخزن‌های شیرپوینت قرار بدید، به محیط طراحی پاورفرم برگردید و آدرس فایل آپلود شده رو توی تنظیمات فرم، توی بخش Printing و داخل فیلد XSLT file url وارد کنید:

تنظیمات فرم پاورفرم رو ذخیره کنید و صفحه‌ای که فرم توش نمایش داده می‌شه رو رفرش کنید. می‌بینید که اکشن‌های مرتبط با پرینت کردن فرم، بالای صفحه ظاهر می‌شن.

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

کدی که توی فایل XSLT نوشتید، در واقع یه لوپه که همه فیلدهای فرم رو توی نمای پرینت نمایش می‌ده، اما در عمل احتمالاً موقع پرینت کردن فرم، نیاز به نمایش همه فیلدهای فرم ندارید یا فقط نیاز دارید تعداد کمی از فیدها رو نمایش بدید. در ادامه می‌بینیم که چطور می‌شه به صورت مشخص، فقط فیلدهایی که لازم داریمو توی نمای پرینت نمایش بدیم.

2. مشخص کردن فیلدها در فایل XSLT

مهم‌ترین چیزی که توی فایل XSLT هست، بخش مربوط به نمایش داده‌های فرمه. با استفاده از تگ‌های <xsl:value-of> می‌تونید داده‌هایی که توی فرم دخیره شدن دریافت کنید و هر جایی که لازم هست نمایش بدید. مثلاً برای نمایش مقدار هر کدوم از فیلدها، می‌تونید از این سینتکس توی فایل XSLT استفاده کنید:

<tr>
     <td colspan="1" class='heading'>عنوان فیلد</td>
     <td colspan="3">
          <xsl:value-of select="ListItem/Control[@ControlName='c_FieldName']"/>
     </td>
</tr>

توی کد بالا باید نام فیلد موجود در فرم رو به جای c_FieldName قرار بدید. نام فیلد رو باید از تنظیمات فیلد توی محیط ویرایش فرم پیدا کنید:

تنظیمات در پاورفرم

توی کد پایین چند تا فیلدی که برای نمایش توی نمای پرینت این فرم نیاز بوده را گذاشتم؛ دقت کنید که این مقادیر توی فرم‌های مختلف متفاوتن و برای همین، برای هر فرم، نیاز به درست کردن یه فایل XSLT مخصوص نمای پرینت همون فرم هست (توی این مثال اسم فیلدها الکی هستن و شما باید اسم فیلدهای فرم خودتونو از توی محیط طراحی فرم پاورفرم پیدا کنید):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     <xsl:output method="xml" encoding="utf-8" indent="no"/>
     <xsl:template match="/">
          <table>
               <tr>
                    <td colspan="1" class='heading'>فیلد اول</td>
                         <td colspan="3">
                              <xsl:value-of select="ListItem/Control[@ControlName='c_FormField1']"/>
                         </td>     
               </tr>
               <tr>
                    <td colspan="1" class='heading'>فیلد دوم</td>
                    <td colspan="3">
                         <xsl:value-of select="ListItem/Control[@ControlName='c_FormField2']"/>
                    </td>
               </tr>
          </table>
     </xsl:template>
</xsl:stylesheet>

3. مقدار دادن به سلول‌های خالی

از جاوااسکریپت برای قرار دادن یه عبارت به جای فیلدهایی که توی فرم پُر نشده‌ن استفاده می‌کنیم؛ این کد چک می‌کنه که محتوای تگ td در html خالی هست یا نه و اگه خالی بود، عبارت «ثبت نشده» رو اون جا نمایش می‌ده و کلس dataNotEntered را به td اضافه می‌کنه؛ بعداً برای تغییر نمایش فیلدهای خالی، می‌تونیم از این کلس استفاده کنیم:

const tdElements = document.querySelectorAll("table td")
tdElements.forEach(function(td) {
     if (!td.innerHTML.trim()) {
          td.innerHTML = "ثبت نشده"
          td.classList.add('dataNotEntered')
     }
})

4. ایجاد تغییرات در ظاهر فرم

با استفاده از تگ style در فایل XSLT می‌تونید مثل هر فایل HTML دیگه‌ای، با استفاده از CSS، استایل‌های اختصاصی برای جدول و فونت و اندازه‌ها و خلاصه هر چیز دیگه‌ای اضافه کنید:

<style>
table {
     direction: rtl;
     width: 100%;
     padding: 1em;
}
td {
     direction: rtl;
     text-align: right;
     font-family: b mitra;
     border: 1px solid;
     padding: .25em;
     min-width: 25%;
     width: 25%;
}
td:not(.heading) {
     font-weight: bold;
}
.heading {
     width: 20%;
     background: #e3efff;
     border: 1px solid #6eacff;
}
.title {
     text-align: center;
     border: none;
     padding-top: 1em;
}
.dataNotEntered {
     color: #5b5b5b;
     border: 1px dashed;
     font-weight: 100 !important;
}
</style>

با اضافه کردن این استایل‌ها می‌تونید نمای چاپی رو دقیقاً مطابق با نیاز خودتون و سازمانتون تنظیم کنید و به راحتی از طریق کد XSLT، نمای پرینت فرم رو به‌صورت حرفه‌ای نمایش بدید.

5. افزودن دکمه چاپ در نمای پرینت

خب، حالا فرم آماده شده. الان می‌تونیم با جاوااسکریپت یه دکمه برای چاپ به فرم اضافه کنیم تا هر کاربری بتون به راحتی وارد پنجره پرینت بشه و فرم رو به صورت مستقیم چاپ کنه. می‌تونید هر جایی از قالب HTML موجود توی فایل XSLT از این دکمه استفاده کنید:

<button onclick="window.print();" class="print">چاپ</button>

اما بعد از کلیک کردن روی این دکمه، توی پنجره‌ای که برای چاپ فرم ظاهر می‌شه، این دکمه لازم نیست که دیده بشه؛ برای همین باید از استایل زیر توی تگ style استفاده کنیم تا موقع چاپ، دکمه مخفی بشه:

@media print {
     .print{
          display:none;
     }
}


تموم شد! با دنبال کردن این مراحل، می‌تونید یه نمای چاپی زیبا و حرفه‌ای برای فرم‌های پاورفرمی شیرپوینت بسازید. قالب‌بندی نهایی کدتون توی فایل XSLT باید این شکلی باشه:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     <xsl:output method="xml" encoding="utf-8" indent="no"/>
     <xsl:template match="/">
          <style>
          table {
               direction: rtl;
               width: 100%;
               padding: 1em;
          }
          td {
               direction: rtl;
               text-align: right;
               font-family: b mitra;
               border: 1px solid;
               padding: .25em;
               min-width: 25%;
               width: 25%;
          }
          td:not(.heading) {
               font-weight: bold;
          }
          .heading{
               width: 20%;
               background: #e3efff;
               border: 1px solid #6eacff;
          }
          .title {
               text-align: center;
               border: none;
               padding-top: 1em;
          }
          .dataNotEntered {
               color: #5b5b5b;
               border: 1px dashed;
               font-weight: 100 !important;
          }
          .print {
               font-family: b mitra;
               border: none;
               color: white;
               font-weight: bolder;
               padding-left: 1em;
               padding-right: 1em;
               border-radius: 3em;
               cursor: pointer;
               background: linear-gradient(90deg, #093e71, #0c95ae, #6ec9ca);
          }
          @media print {
               .print{
                    display:none;
               }
          }
          </style>

          <table style="border: 2px das">
               <tr>
                    <td colspan="1" class='heading'>فیلد اول</td>
                         <td colspan="3">
                              <xsl:value-of select="ListItem/Control[@ControlName='c_FormField1']"/>
                         </td>     
               </tr>
               <tr>
                    <td colspan="1" class='heading'>فیلد دوم</td>
                    <td colspan="3">
                         <xsl:value-of select="ListItem/Control[@ControlName='c_FormField2']"/>
                    </td>
               </tr>
          </table>

          <script>
          const tdElements = document.querySelectorAll("table td")
          tdElements.forEach(function(td) {
               if (!td.innerHTML.trim()) {
                    td.innerHTML = "ثبت نشده"
                    td.classList.add('dataNotEntered')
               }
          })
           </script>
     </xsl:template>
</xsl:stylesheet>

برطرف کردن خطای 8321 در سرور شیرپوینت

اگر شما هم از شیرپوینت روی یه سروری که به اینترنت متصل نیست استفاده می‌کنید، دیر یا زود با این خطا در Event Viewer مواجه خواهید شد:

A certificate validation operation took X milliseconds and has exceeded the execution time threshold. If this continues to occur, it may represent a configuration issue. Please see http://go.microsoft.com/fwlink/?LinkId=246987 for more details.

این خطا که در قالب یه Critical Error ظاهر می‌شه، وقتی پیش میاد که شیرپوینت می‌خواد گواهی‌های SSL رو چک کنه و از طریق بررسی‌های آنلاین تأیید کنه که معتبر هستن یا نه؛ اما چون سرور شما به اینترنت وصل نیست، این ارور رو می‌بینید. شیرپوینت از SSL/TLS استفاده می‌کنه تا مطمئن بشه گواهی‌نامه‌ها معتبر هستن و این فرایند شامل این دو مورده:

  • CRL (لیست لغو گواهی): بررسی یه لیست که نشون می‌ده کدوم گواهی‌ها نامعتبر شدن.
  • OCSP (پروتکل وضعیت گواهی آنلاین): چک کردن لحظه‌ای وضعیت گواهی از مرجع صادرکننده گواهی‌نامه.

طبیعیه که شیرپوینت برای چک کردن این موارد به اینترنت نیاز داره و وقتی سرور به اینترنت دسترسی نداره، این چک‌ها به نتیجه‌ای نمی‌رسه و باعث می‌شه خطای اعتبارسنجی گواهی یا همون خطای 8321 به وجود بیاد؛ این خطا معمولاً هر پانزده تا بیست دقیقه، موقع لاگین کردن کاربرها و بازدید کردن اون‌ها از بخش‌های مختلف سایت‌های شیرپوینتی روی Event Viewer ظاهر می‌شه و در بعضی موارد باعث می‌شه سرعت لود صفحات شیرپوینت پایین بیاد.

چطور این خطا رو رفع کنیم؟

برای رفع این مشکل، می‌تونید عملیات چک کردن گواهی رو از طریق تنظیمات Group Policy غیرفعال کنید؛ این روش ساده‌ترین و موثرترین راه حل برای سرورهای بدون اینترنته. این مراحل رو به ترتیب انجام بدید:

  • روی سرور دکمه استارت رو بزنید، edit group policy رو تایپ کنید و Enter رو بزنید تا Group Policy Editor باز بشه.
  • روی Computer Configuration دبل‌کلیک کنید.
  • روی Windows Settings دبل‌کلیک کنید.
  • روی Security Settings دبل کلیک کنید.
  • روی Public Key Policies دبل‌کلیک کنید.
  • روی Certificate Path Validation Settings دبل‌کلیک کنید.
  • به تب Network Retrieval برید.
  • تیک Define These Policy Settings رو بزنید.
  • تیک Automatically update certificates in the Microsoft Root Certificate Program (recommended) رو بردارید.
  • اوکی رو بزنید و پنجره رو ببندید.
تنظیمات در سرور شیرپوینت

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

  • یه پنجره CMD با دسترسی ادمین باز کنید.
  • تایپ کنید: gpupdate /force و اینتر بزنید.
  • بعد از اتمام عملیات، سرور رو ریست کنید.

بررسی رفع مشکل

تنظیماتی که انجام دادید باید جلوی تلاش‌های شیرپوینت برای چک‌های آنلاین گواهی رو بگیره و باعث بشه دیگه این خطا رو دریافت نکنید. بعد از بالا اومدن سرور به Event Viewer سر بزنید و ببینید که آیا خطا همچنان وجود داره یا نه. اگر ارور هنوز بود، دوباره تنظیماتی که توی Group Policy انجام دادید رو چک کنید که درست اعمال شده باشن.

جمع‌بندی

مشکل تایم اوت در اعتبارسنجی گواهی در شیرپوینت سرور، وقتی سرور به اینترنت وصل نیست، یه مشکل رایجه؛ با غیرفعال کردن این چک‌ها از طریق Group Policy می‌تونید این مشکل رو برطرف کنید و عملکرد سرور خودتون رو بهبود بدید؛ برای مدیران شیرپوینت که سرورهای آفلاین دارن، این تنظیمات باعث می‌شه سرور بدون تأخیرهای اضافی ناشی از درخواست‌های غیرقابل‌دسترس کار کنه و خطاهای غیرضروری از بین بره.

ضمناً برای انجام این تغییرات لازم نیست اصلاً نگران باشید. توی سرورهای شیرپوینتی که به اینترنت دسترسی ندارن، غیرفعال کردن این چک‌ها معمولاً امنه و خطری نداره چون اینترنتی وجود نداره که بخواد اعتبارسنجی انجام بشه؛ ولی برای سرورهای آنلاین بهتره این چک‌ها فعال بمونن تا سرور امنیت بالاتری داشته باشه.

2 روش ساده برای تبدیل تاریخ میلادی به شمسی در Power BI

اگه شما هم مثل من با دیتاست‌های فارسی توی Power BI کار کرده باشید، حتماً با این چالش برخورد کردید که چطور تاریخ‌های میلادی رو به شمسی تبدیل کنید و می‌دونید که چقدر انجام دادن این کار برای تهیه گزارش حیاتیه؛ من هم اینو گوگل کردم: تبدیل تاریخ میلادی به شمسی در Power BI اما راهنمای ساده و سرراستی پیدا نکردم که به دردم بخوره، برای همین تصمیم گرفتم درباره تبدیل تاریخ میلادی به شمسی در Power BI بنویسم تا شاید لازم نباشه بقیه اندازه من برای پیدا کردن بهترین راه حل وقت بذارن.

نرم افزار Power BI به صورت پیش‌فرض فقط از تاریخ میلادی پشتیبانی می‌کنه؛ راه‌های مختلفی برای فارسی کردن تاریخ و حل این مشکل وجود داره. توی این مطلب درباره دو تا از روش‌های ساده‌ای که خودم همیشه برای تبدیل تاریخ‌ها ازشون استفاده می‌کنم می‌نویسم:

روش اول: نمایش تاریخ به صورت محلی (Using Locale)

Using Locale در Power BI یه قابلیتیه که به کارشناس داده اجازه می‌ده فرمت اطلاعاتی مثل تاریخ و واحد پول رو بر اساس تنظیمات زبانی و فرهنگی مختلف تغییر بده. مثلاً توی ایالات متحده امریکا تاریخ معمولاً به شکل ماه / روز / سال یعنی YYYY/DD/MM نمایش داده می‌شه، اما توی کشورهای اروپایی تاریخ رسمی به شکل روز / ماه / سال یعنی YYYY/MM/DD استفاده می‌شه؛ از این قابلیت Power BI می‌شه برای تبدیل تاریخ میلادی به تاریخ شمسی هم استفاده کرد.

کافیه وقتی که در حال دریافت اطلاعات توی محیط Power Query Editor هستید، روی عنوان ستونی که قراره تاریخش شمسی بشه، راست کلیک کنید و از Change Type، گزینه آخر یعنی Using Locale رو انتخاب کنید.

توی پنجره‌ی باز شده، فیلد Locale را به Persian تغییر بدید و OK رو بزنید تا به همین راحتی ستون انتخاب شده فارسی بشه.

واقعاً ساده نبود؟ حالا یه روش دیگه که به همین اندازه ساده نیست ولی ممکنه یه جاهایی به دردتون بخوره:

روش ساختن ستون جدید با استفاده از زبان M

اگه بخوام خیلی ساده توضیح بدم، زبان M زبانی برای کار کردن با اطلاعاته؛ این زبان برای انجام تغییرات در داده‌هاییه که از منابع مختلف دریافت می‌کنید و به غیر از Power BI توی نرم افزارهای دیگه‌ای مثل اکسل هم کاربرد داره؛ توی این روش در Power Query Editor با استفاده از زبان M، یه ستون جدید می‌سازیم و داخلش، تاریخ میلادی رو به شمسی تبدیل می‌کنیم.

برای تبدیل تاریخ میلادی به شمسی با استفاده از زبان M باید:

  • وارد محیط Power Query Editor بشید
  • از منوی سمت چپ روی کوئری مورد نظر کلیک راست کنید
  • روی Advanced Editor کلیک کنید تا پنجره ویرایش کوئری باز بشه
  • این دو خط کوئری را به انتهای متن موجود و درست قبل از in اضافه کنید
#"Converted to Date" = Table.TransformColumns(#"Removed Columns", {{"Date", Date.From}}),
#"Converted to Jalali" = Table.AddColumn(#"Converted to Date", "Jalali Date", each Date.ToText([Date], "yyyy/MM/dd", "fa-IR"))

دقت کنید که توی این کوئری، توی خط اول به جای "Date" و توی خط دوم به جای [Date]، اسم ستون تاریخ میلادی خودتونو وارد کنید؛ توی خط آخر کوئری و پس از in هم، همیشه باید اسم آخرین مرحله از کوئری که این جا Converted to Jalali هست رو وارد کنید. در نهایت دکمه Done رو بزنید:

تبدیل تاریخ میلادی به شمسی در Power BI

می‌بینید که یه ستون جدید به نام Jalali Date به انتهای اطلاعات شما اضافه شده که شامل تاریخ شمسی تبدیل شده‌س:

با استفاده از این دو روش، به راحتی می‌تونید توی گزارشاتون تاریخ شمسی داشته باشید.

امیدوارم این مطلب به دردتون خورده باشه. یادتون نره اگه سؤال یا نظری داشتید پایین همین صفحه می‌تونید برام کامنت بذارید و اگر نیاز بود که باهام تماس بگیرید می‌تونید از لینک‌های آخر صفحه استفاده کنید.

ساختن پورتال سازمانی فارسی شیرپوینت در 5 قدم

توی این پست قراره کامل و قدم‌به‌قدم توضیح بدم که چطوری می‌شه یه پورتال سازمانی فارسی شیرپوینت طراحی و پیاده سازی کرد.

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

قبل از هر چیزی مطمئن بشید که قالب سایت شیرپوینتی که در اختیارتون قرار دادن از نوع publishing site هست؛ اگر این طور نبود، از ادمین شیرپوینت سازمانتون در خواست کنید براتون یه سایت کالکشن با آدرس دلخواهتون بسازه و تأکید کنید که قالب سایت کالکشنی که ساخته می‌شه، حتماً از نوع publishing site باشه. وقتی سایت رو تحویل گرفتید می‌تونید شروع کنید به ساختن پورتال سازمانی فارسی شیرپوینت!

1. ساختن مسترپیجِ پورتال سازمانی فارسی شیرپوینت

مسترپیج (Masterpage) در شیرپوینت در واقع قالبیه که شامل طرح‌بندی کلی همه صفحات یک سایته. بخش‌هایی از صفحه که توی همه صفحات مشترک هستن داخل قالب مسترپیج قرار می‌گیرن؛ مثلاً:

  • هدر
  • فوتر
  • لوگو

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

قدم اول ما هم اینه که مسترپیجی که قراره پورتال از اون استفاده کنه رو بسازیم. برای این کار روی آیکون چرخ‌دنده بالای صفحه کلیک می‌کنیم و از اون جا وارد بخش Design Manager می‌شیم. از منوی موجود توی این صفحه استفاده می‌کنیم و روی گزینه شماره سه، یعنی Upload Design Files کلیک می‌کنیم می‌کنیم تا بتونم فایل‌های مرتبط به طراحی رو روی سیستم خودمون مَپ کنیم؛ این طوری به فایل‌ها خیلی راحت‌تر دسترسی خواهیم داشت و فرایند توسعه و پیاده‌سازی پورتال سازمانی خیلی سریع‌تر انجام می‌شه. پس:

  • روی گزینه Upload Design Files کلیک کنید.
  • آدرسی که توی صفحه میاد رو کپی کنید (معمولاً توش کلمه masterpage هست).
  • روی سیستم خودتون This PC (یا همون my computer) رو باز کنید.
  • از منوی سمت چپ روی Network، کلیک راست کنید و Map network drive رو بزنید.
  • آدرس کپی شده رو اون جا وارد کنید و Finish رو بزنید.
  • مشخصات اکانت ادمین رو وارد کنید و دکمه ثبت رو بزنید.
برای ساختن پورتال فارسی در شیرپوینت باید اول از همه فولد مرتبط با مسترپیج رو روی سیستم خودتون مپ کنید

از این فولدری که این جا براتون باز می‌شه می‌تونید استفاده کنید تا به راحتی به فایل‌ها و فولدرهای مرتبط با طراحی سایت دسترسی داشته باشید؛ حتماً می‌دونید که از طریق تنظیمات شیرپوینت هم می‌تونید به این فایل‌ها دسترسی داشته باشید، اما بر اساس تجربه من، این روش راحت‌ترین و سریع‌ترین راه برای دسترسی به فایل‌های مرتبط با مسترپیج و ویرایش کردن بخشای مختلف مسترپیجه.

حالا برمی‌گردیم به بخش Design Manager و روی لینک چهارم فهرست، یعنی Edit Master Pages، کلیک می‌کنیم. توی این صفحه روی لینک Create a minimal master page کلیک می‌کنیم تا سیستم برای ما یک مسترپیج ساده درست کنه؛ اسمش رو انتخاب کنید و اوکی رو بزنید.

بعد از چند ثانیه، می‌بینید که مسترپیجی که درست کردید به فهرست مسترپیج‌ها اضافه شده و مهم‌تر از همه، یک فایل HTML هم‌نام با این مسترپیج، توی فولدری که روی سیستم خودتون مپ کردید نمایش داده می‌شه؛ این فایل HTML مسترپیج، فایلیه که توی همه صفحات سایت فراخوانی می‌شه و بنابراین جای مناسبیه برای فراخونی منابع و فایل‌های دیگه‌ای که نیاز دارید توی همه صفحات سایت استفاده کنید؛ مثلاً فایل‌ها و کتابخونه‌های مرتبط با رابط کاربری.

داخل فولدری که مَپ کردیم یک فولدر به نام Theme Files درست کنید، فایل‌های مورد نیازتون رو توی این فولدر قرار بدید و بعد فایل‌هایی که نیاز دارید توی همه صفحات سایت لود بشن رو توی فایل مسترپیج زیر کامنت CE: End Head Snippet فراخونی کنید:

<!--CE: End Head Snippet-->
<link href="./Theme Files/css/icon.css" rel="stylesheet" />
<link type="text/css" rel="stylesheet" href="./Theme Files/css/materialize-rtl.min.css" media="screen,projection" />
<link type="text/css" rel="stylesheet" href="./Theme Files/css/style.css" media="screen,projection" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="text/javascript" src="./Theme Files/js/jquery-3.5.0.min.js">//<![CDATA[//]]></script>
<script type="text/javascript" src="./Theme Files/js/materialize.min.js">//<![CDATA[//]]></script>
<script type="text/javascript" src="./Theme Files/js/footer.js">//<![CDATA[//]]></script>
<!--[if gte mso 9]><xml>

توی این مسترپیج، من به چند تا فایل جاوااسکریپت نیاز داشتم: جی‌کوئری و متریالایز رو اضافه کردم و یک فایل به نام footer.js هم فراخونی کردم (چون فوتر توی همه صفحات باید وجود داشته باشه).

یادآوری می‌کنم که باید هر چیزی که نیاز دارید توی همه صفحات تکرار بشه، مثلاً لوگو، منو یا هر چیزی که به هدر و فوتر سایت مرتبط هست رو توی این صفحه مسترپیج که در واقع یک صفحه HTML هست، توسعه بدید و پیاده‌سازی کنید؛ این طوری دیگه لازم نیست مثلاً لوگوی سایت رو توی همه صفحات به صورت جداگونه فراخونی کنید؛ همچنین اگه بعداً لازم شد مثلاً فوترو تغییر بدید، فقط یک جا لازمه تغییر بدید و تغییر شما توی همه صفحات اعمال می‌شه.

برای این که تست کنید و ببینید که فایل‌هاتون درست لود می‌شن یا طرح مسترپیجتون درست پیاده‌سازی شده یا نه، باید دوباره وارد صفحه Design Manager بشید و اول از قسمت چهارم مسترپیجتون رو منتشر کنید (Approved) و بعد از قسمت هفتم (Publish and apply design) روی لینک Assign master page to your site کلیک کنید مسترپیج جدیدتون رو به سایت اعمال کنید.

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

مسترپیج رو به پورتال سازمانی فارسی شیرپوینت اعمال کنید

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

2. اضافه کردن لوگو، هدر و فوتر

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

بریم سراغ لوگو. برای اضافه کردن لوگو باید:

  • از Design Manager روی لینک چهارم، یعنی Edit Master Pages کلیک کنید.
  • روی اسم مسترپیجی که روی صفحات سایت اعمال کردید کلیک کنید.
  • توی صفحه جدیدی که باز می‌شه از منوی بالا روی لینک Snippets کلیک کنید.
  • از منوی Design روی گزینه Site Logo کلیک کنید.
  • اسنیپتی که توی صفحه ظاهر می‌شه رو کپی کنید.
  • این اسنیپت رو توی صفحه HTML مسترپیج بذارید و صفحه رو ذخیره کنید.

سعی کنید خطوط و تگ‌های پیشفرض رو از صفحه HTML مسترپیج پاک نکنید (مخصوصاً چیزایی که نمی‌دونید چی هستن). بعد از ذخیره کردن مسترپیج به صفحه اصلی سایت برید و ببینید که لوگو چطوری توی پورتال قرار گرفته.

از الان به بعد می‌تونید لوگوی سایت رو از تنظیماتش عوض کنید؛ برای این کار کافیه به قسمت تنظیمات سایت برید و روی Title description and logo کلیک کنید؛ توی این صفحه هر لوگویی که برای سایتتون انتخاب کنید، توسط اسنیپتی که داخل مسترپیج قرار دادید، به شکل خودکار توی همه صفحات سایت قرار می‌گیره؛ اگر لازم داشتید از همین اسنیپت توی فوتر سایت یا هر جای دیگه هم می‌تونید استفاده کنید و اگر حوصله داشته باشید با CSS می‌تونید هر استایلی که دوست دارید بهش اعمال کنید.

هر چقدر حجم تصویری که به عنوان لوگو انتخاب می‌کنید کم‌تر باشه بهتره (مثلاً حداکثر صد کیلوبایت باشه).

هدر و فوتر هم توی فایل HTML مسترپیج بنویسید. من معمولاً از JSOM برای اضافه کردن آیتم‌هایی توی هدر و فوتر استفاده می‌کنم، برای همین هم فایل footer.js رو توی مسترپیج فراخونی کردم. توی بخش سوم توضیح می‌دم که چطوری می‌تونید از JSOM برای اضافه کردن بخش‌های مختلف به پورتال استفاده کنید.

3. اضافه کردن بخش‌های مختلف پورتال با فناوری JSOM

با اضافه کردن بخش‌های مختلف به پورتال و شخصی‌سازی صفحه اصلی پورتال سازمانی فارسی شیرپوینت، تجربه کاربری استفاده‌کننده‌ها و ادمین‌های پورتال ارتقا پیدا می‌کنه. JSOM فناوری خوبیه که من همیشه برای شخصی‌سازی صفحات شیرپوینت استفاده می‌کنم. با JSOM می‌تونید با داده‌های موجود در لیست‌ها و سایت‌های مختلف شیرپوینتتون تعامل داشته باشید؛ الان قراره از این فناوری استفاده کنیم و بخش «لینک‌های سامانه‌ها» رو توی پورتال اضافه کنیم.

دقت کنید که موضوع این آموزش JSOM نیست ولی من قبلاً یه مطلب دیگه درباره JSOM نوشتم که می‌تونید از این لینک مطالعه‌ش کنید و درباره‌ش اطلاعات بیشتری به دست بیارید.

بخش لینک‌های سامانه‌ها که معمولاً توی همه پورتال‌های سازمانی هست، مثل لوگو یا هدر یا فوتر نیست که توی همه صفحات تکرار بشه و فقط قراره توی صفحه اول نمایش داده بشه؛ برای همین قراره یه قالب صفحه درست کنیم و قالب رو روی صفحه اول اعمال کنیم؛ برای این کار برمی‌گردیم توی صفحه Design Manager و لینک شماره شش فهرست یعنی Edit Page Layouts رو می‌زنیم؛ توی این صفحه روی لینک Create page layout کلیک می‌کنیم، اسم قالب رو می‌نویسیم و همچنین مسترپیجی که خودمون ساختیمو انتخاب می‌کنیم. اگه به فولدری که روی سیستم مَپ کردید نگاه کنید می‌بینید که به همین سادگی فایل HTML قالب جدیدی که ساختید ایجاد شده (دقیقاً مثل ساخته شدن مسترپیج).

توی قالب صفحه‌ای که الان ساختید، قراره با استفاده از فناوری JSOM بخش لینک‌های سامانه‌ها رو درست کنیم. اگر بخوایم JSOM بنویسیم باید جاوااسکریپت بنویسیم، بنابراین نیاز داریم که یه فایل جاوااسکریپت بسازیم و توی این صفحه فراخونیش کنیم. من داخل فولدر js که قبلاً ساختم، یه فایل جدید می‌سازم به نام home.js و توی صفحه قالبم که اسمش رو گذاشتم home.html در انتهای فایل، قبل از بسته شدن تگ body فراخونیش می‌کنم:

          <script type="text/javascript" src="./Theme Files/js/home.js">//<![CDATA[//]]></script>
     <!--ME:</asp:ContentPlaceHolder>-->
     <!--MS:<asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server">-->
     <!--ME:</asp:ContentPlaceHolder>-->
</body>

توی همین صفحه، جایی که قراره لینک‌ها قرار بگیرن هم باید با یه آی‌دی مشخص بشه که بعداً بتونیم از این آی‌دی توی کُد جاوااسکریپت استفاده کنیم:

<div class="row mb0">
     <div class="col s12">
          <div class="row">
               <div class="col s10 offset-s1">
                    <div class="row" id="homeMainLinks"></div>
               </div>
          </div>
     </div>
</div>

بعد از ذخیره کردن این فایل، به صفحه Design Manager برمی‌گردم و قالب اضافه شده رو منتشر می‌کنم. بعد به صفحه اصلی سایت می‌رم و وارد حالت ویرایش صفحه می‌شم و با استفاده از تنظیمات Page Layout، قالب جدیدی که ساختمو روی صفحه اعمال می‌کنم.

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

حالا توی فایل home.js یه کد JSOM می‌نویسم تا از لیستی که قبلاً ساخته شده، اطلاعات لینک‌ها رو فراخونی کنه و نمایش بده؛ برای این کار توی فایل home.js از jQuery و JSOM استفاده می‌کنم:

$(document).ready(function() {
     SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
          function getMainLinks() {

               // context
               var targetSiteUrl = 'آدرس سایت رو این جا وارد کنید'
               var context = new SP.ClientContext(targetSiteUrl)
               var web = context.get_web()

               // home main links
               var listMainLinks = web.get_lists().getByTitle('اسم لیست رو این جا وارد کنید')
               var camlMainLinks = new SP.CamlQuery()
               var camlStringMainLinks = 
               '<View>' +
                    '<Query>' +
                         '<OrderBy>' +
                              '<FieldRef Name="SPSArrangement" Ascending="TRUE" />' +
                         '</OrderBy>' +
                    '</Query>' +
               '</View>'
               camlMainLinks.set_viewXml(camlStringMainLinks)
               var itemMainLinks = listMainLinks.getItems(camlMainLinks)
               context.load(itemMainLinks)

               context.executeQueryAsync(
                    function() {

                         // home main links
                         var listItemsMainLinks = itemMainLinks.getEnumerator()                
                         while (listItemsMainLinks.moveNext()) {
                              var listItem = listItemsMainLinks.get_current()
                              var itemTitle = listItem.get_fieldValues().Title
                              var itemImage = listItem.get_fieldValues().SPSImage
                              var itemLinks = listItem.get_fieldValues().SPSLinkAddress

                              $('#homeMainLinks').append(
                                   '<div class="eachMainLink mb3">' +
                                        '<a href="' + itemLinks + '" target="_blank">' +
                                        itemImage +
                                        '<p class="center-align mt0">' + itemTitle + '</p>' +
                                   '</a>' +
                                   '</div>'
                              )
                         }
                    },
                    function(sender, args) { // On failure
                         console.log('Request failed: ' + args.get_message())
                    }
               );
          }
        
          // Call the function when the page is ready
          getMainLinks()

     })
})

به صورت خیلی خلاصه کاری که این کد انجام می‌ده اینه که:

  • منتظر می‌مونه تا صفحه کامل لود بشه.
  • منتظر می‌مونه تا فایل sp.js کامل لود بشه.
  • به سایتی که اطلاعات لینک‌ها داخلش هستن وصل می‌شه.
  • اطلاعات رو از لیستی که عنوانش رو مشخص کردیم دریافت می‌کنه.
  • از یک کوئری CAML استفاده می‌کنه تا ترتیب مواردو مشخص کنه.
  • موارد دریافت شده رو با استفاده از آی‌دی داخل HTML قرار می‌ده.

دیدید؟ به همین راحتی تونستید لینک‌ها رو توی پورتال سازمانی فارسی شیرپوینت قرار بدید!

حالا فرض کنید که قراره یه بخش دیگه هم توی پورتال داشته باشیم: مثلاً بخش آخرین اخبار؛ برای اضافه کردن آخرین اخبار هم باید یک لیست اخبار توی شیرپوینت داشته باشیم که ادمین شیرپوینت بتونه اخبارو اون جا وارد کنه و به محض وارد کردن اطلاعات توسط ادمین، بخش اخبار پورتال بروزرسانی بشه؛ برای این کار باید یه چیزایی رو به کد HTML موجود توی صفحه home.html و همچنین کد JSOM موجود در صفحه home.js اضافه کنیم.

<!-- home main links -->
<div class="row mb0">
     <div class="col s12">
          <div class="row pt5 mb0">
               <div class="col s10 offset-s1">
                    <div class="row" id="homeMainLinks"></div>
               </div>
          </div>
     </div>
</div>
<!-- home main links -->

<!-- news -->
<div class="row mb0">
     <div class="col s12">
          <div class="row pt5 pb5 px5 mb0" id="homeNewsContainer">
               <div class="mt3 mb3">
                    <h3 class="pr1">آخرین اخبار</h3>
               </div>
          </div>
     </div>
</div>
<!-- news -->
$(document).ready(function() {
     SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
          function getMainLinks() {

               // context
               var targetSiteUrl = 'آدرس سایت رو این جا وارد کنید'
               var context = new SP.ClientContext(targetSiteUrl)
               var web = context.get_web()

               // home main links
               var listMainLinks = web.get_lists().getByTitle('اسم لیست رو این جا وارد کنید')
               var camlMainLinks = new SP.CamlQuery()
               var camlStringMainLinks = 
               '<View>' +
                    '<Query>' +
                         '<OrderBy>' +
                              '<FieldRef Name="SPSArrangement" Ascending="TRUE" />' +
                         '</OrderBy>' +
                    '</Query>' +
               '</View>'
               camlMainLinks.set_viewXml(camlStringMainLinks)
               var itemMainLinks = listMainLinks.getItems(camlMainLinks)
               context.load(itemMainLinks)

               // news
               var listNews = web.get_lists().getByTitle('اخبار')
               var camlNews = new SP.CamlQuery()
               var camlStringNews = 
               '<View>' +
                    '<Query>' +
                         '<OrderBy>' +
                              '<FieldRef Name="Created" Ascending="FALSE" />' +
                    '</OrderBy>' +
                    '</Query>' +
                    '<RowLimit>4</RowLimit>' +
               '</View>'
               camlNews.set_viewXml(camlStringNews)
               var itemNews = listNews.getItems(camlNews)
               context.load(itemNews)

               context.executeQueryAsync(
                    function() {

                         // home main links
                         var listItemsMainLinks = itemMainLinks.getEnumerator()                
                         while (listItemsMainLinks.moveNext()) {
                              var listItem = listItemsMainLinks.get_current()
                              var itemTitle = listItem.get_fieldValues().Title
                              var itemImage = listItem.get_fieldValues().SPSImage
                              var itemLinks = listItem.get_fieldValues().SPSLinkAddress

                              $('#homeMainLinks').append(
                                   '<div class="eachMainLink mb3">' +
                                        '<a href="' + itemLinks + '" target="_blank">' +
                                        itemImage +
                                        '<p class="center-align mt0">' + itemTitle + '</p>' +
                                   '</a>' +
                                   '</div>'
                              )
                         }

                         // news
                         var listItemsNews = itemNews.getEnumerator()                
                         while (listItemsNews.moveNext()) {
                              var listItem = listItemsNews.get_current()
                              var itemIdddd = listItem.get_fieldValues().ID
                              var itemTitle = listItem.get_fieldValues().Title
                              var itemDescr = listItem.get_fieldValues().SPSDescription
                              var itemDates = listItem.get_fieldValues().Created
                              var itemImage = listItem.get_fieldValues().SPSImage
                              var persianTi = new Date(itemDates).toLocaleDateString('fa-IR')

                              $('#homeNewsContainer').append(
                                   '<div class="col s3">' +
                                        '<a href="#" target="_blank">' +
                                             itemImage +
                                             '<h5>' + itemTitle + '</h5>' +
                                             '<p>' + itemDescr + '</p>' +
                                             '<p class="homeNewsDate">' + persianTi + '</p>' +
                                        '</a>' +
                                   '</div>'
                              )
                         }

                    },
                    function(sender, args) { // On failure
                         console.log('Request failed: ' + args.get_message())
                    }
               );
          }
        
          // Call the function when the page is ready
          getMainLinks()

     })
})

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

این دو بخش رو به عنوان نمونه‌ای برای فهمیدن ساختار درست استفاده از JSOM این جا آوردم؛ شما به همین ترتیب می‌تونید بخش‌های مختلف دیگه‌ای مثل سخن روز، تصویر روز، پیام‌های مرتبط با تبریک تولد همکاران یا تسلیت به همکاران و هر ماژول مفید دیگه‌ای که فکرشو بکنید، به صفحه پورتال فارسی شیرپوینت اضافه کنید.

4. اضافه کردن فونت دلخواه به پورتال سازمانی فارسی شیرپوینت

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

من معمولاً از فونت‌هایی با فرمت ttf یا woff یا woff2 استفاده می‌کنم؛ این‌ فرمت‌ها فرمت‌های بهینه‌سازی شده برای وب هستن.

اول از همه فونت‌ها رو توی یکی از فولدرها، توی فولدری که قبلاً مپ کردیم اضافه می‌کنم. اگر دوست داشته باشید می‌تونید از یه مخزن اسناد برای آپلود کردن فایل فونت‌ها استفاده کنید و از آدرس اون مخزن اسناد برای رفرنس دادن استفاده کنید. مهم اینه که فایل‌ها جایی آپلود شده باشن که کاربری که به سایت مراجعه می‌کنه بهش دسترسی داشته باشه.

بعد با استفاده از CSS فایل‌هایی که اضافه کردمو توی صفحه فراخونی می‌کنم. قبلاً فایل CSS رو به نام style.css ساختم و همون طور که توی مرحله اول دیدید، اون رو توی فایل مسترپیج فراخونی کردم؛ بنابراین داخل فایل style.css می‌تونم فونت‌ها رو ثبت کنم و بعد هر جایی که می‌خوام ازشون استفاده کنم:

/*fonts*/
@font-face {
     font-family: 'peyda';
     src: url('fonts/peydaa/peydaWeb-light.eot');
     src:
          url('fonts/peydaa/peydaWeb-light.woff2') format('woff2'),
          url('fonts/peydaa/peydaWeb-light.woff') format('woff');
     font-weight: 100;
}
@font-face {
     font-family: 'peyda';
     src: url('fonts/peydaa/PeydaWeb-Medium.eot');
     src:
          url('fonts/peydaa/PeydaWeb-Medium.woff2') format('woff2'),
          url('fonts/peydaa/PeydaWeb-Medium.woff') format('woff');
     font-weight: 500;
}
@font-face {
     font-family: 'peyda';
     src: url('fonts/peydaa/PeydaWeb-Bold.eot');
     src:
          url('fonts/peydaa/PeydaWeb-Bold.woff2') format('woff2'),
          url('fonts/peydaa/PeydaWeb-Bold.woff') format('woff');
     font-weight: 900;
}
body, h1, h2, h3, h4, h5, h6, a, .s4-workspace * {
     font-family: 'peyda', serif;
}
/*fonts*/

شما باید اون جایی که url ثبت می‌شه، آدرس فایل‌هایی که آپلود کردید رو بذارید.

5. منتشر کردن فایل‌های مرتبط با پورتال سازمانی فارسی شیرپوینت

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

همه فایل‌ها باید منتشر بشن و هیچ چیزی نباید از قلم بیفته؛ دقت کنید که به صورت پیشفرض در شیرپوینت، همه صفحات، وب‌پارت‌ها، فایل‌های CSS و JS، تصاویر، قالب‌های صفحات و در نهایت مسترپیج، قبل از این که برای عموم کاربران در دسترس و قابل مشاهده باشن، باید منتشر بشن. برای انجام عملیات انتشار باید به ترتیب این مواردو انجام بدید:

  • صفحه‌ای که درست کردید رو چند بار چک کنید که مشکلی نداشته باشه.
  • به صفحه Design Manager برید.
  • از طریق فهرست، روی Edit Master Pages کلیک کنید.
  • مسترپیجی که ساختید رو منتشر کنید.
  • از طریق فهرست، روی Edit Page Layouts کلیک کنید.
  • قالب یا قالب‌هایی که ساختید رو منتشر کنید.
  • از طریق فهرست، روی Publish and Apply Design کلیک کنید.
  • روی لینکی که انتهای پاراگراف اول کلیک کنید.
  • فولدر به فولدر همه فایل‌هایی که اضافه کردیدو منتشر کنید.
  • به صفحه Design Manager برگردید.
  • برای آخرین بار، مسترپیج رو به صفحات سایت اعمال کنید.
  • به مخزن اسنادی که صفحات سایت داخلش هست برید.
  • اون جا همه صفحاتی که جدید ساختید رو منتشر کنید.

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

خلاصه کارهایی که انجام دادیم

توی این پست نکات مهم زیادی مطرح شد ولی از همه مهم‌تر یاد گرفتیم که چطوری:

  • قالب مسترپیج و قالب صفحه بسازیم
  • فایل‌هایی مورد نیازمونو داخل مسترپیج قرار بدیم
  • اسنیپت لوگو رو به مسترپیج اضافه کنیم
  • مسترپیج رو به صفحات سایتی اعمال کنیم
  • موارد لیست شیرپوینت رو با JSOM فراخونی کنیم
  • فونت فارسی دلخواهمون رو به پورتال اضافه کنیم
  • فایل‌ها رو منتشر کنیم

در نظر داشته باشید که شیرپوینت سیستم خیلی انعطاف‌پذیریه و همیشه می‌تونید هر طوری که مد نظرتون باشه ازش استفاده کنید، توسعه‌ش بدید و قابلیت‌های جدید بهش اضافه کنید.

اگر درباره شیرپوینت سازمانتون نیاز به مشورت دارید، از لینک‌های پایین صفحه استفاده کنید و باهام تماس بگیرید. اگر درباره این مطلب هم سؤال دارید می‌تونید همین زیر کامنت بذارید و سؤالتونو بپرسید. خوشحال می‌شم بتونم کمک کنم!

استفاده از JSOM در شیرپوینت

JSOM چیه؟

یکی از راه‌های مدیریت دیتا توی شیرپوینت، استفاده کردن از JSOM در شیرپوینت برای خوندن، ویرایش، حذف و اضافه کردن دیتاس؛ این کتابخونه به توسعه‌دهنده‌ها اجازه می‌ده کارای دیگه‌ای مثل تغییر دسترسی‌ها کاربرای مختلف هم انجام بدن.

برای استفاده کردن از JSOM در شیرپوینت فقط نیاز دارید که فایل sp.js رو توی صفحه فراخونی کنید و شروع کنید به کد زدن. برای فراخونی فایل sp.js راه‌های مختلفی وجود داره؛ مثلاً می‌شه از وب پارت content editor یا content search استفاده کرد. فرض من اینه که شما توسعه‌دهنده شیرپوینت هستید و با نحوه کار کردن display templateها توی شیرپوینت آشنایی دارید؛ همچنین به راحتی با HTML و CSS و jQuery کار می‌کنید.

توی این مقاله من از یه وب پارت content search استفاده کردم؛ البته راه‌های سرراست‌تری هم برای استفاده از JSOM در شیرپوینت وجود داره؛ مثلاً توی پست ساختن پورتال سازمانی فارسی از JSOM توی قالب صفحه استفاده کردیم؛ توی display templateهای پیش‌فرض شیرپوینت هم روش‌هایی برای فراخونی فایل‌های CSS و JS وجود داره که جلوتر می‌بینید.

هر وب پارت content search از دو تا فایل HTML ساخته شده: فایل کنترل و فایل آیتم (این فایل‌ها display template نامیده می‌شن). فایل کنترل فقط یک بار توی مارک‌آپ صفحه تکرار می‌شه و ساختار کلی نتایج سرچ رو مشخص می‌کنه. فایل آیتم، به ازای هر آیتمی که سرچ برگردونه تکرار می‌شه. کدی که من می‌خوام بنویسم لازمه که فقط یک بار در صفحه اجرا بشه، بنابراین یه فایل جاوااسکریپت می‌سازم و توی فایل کنترلم، اون رو فراخونی می‌کنم:

<script>
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/css/select2.min.css");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/css/persian-datepicker.min.css");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/js/select2.min.js");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/js/persian-date.min.js");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/js/persian-datepicker.min.js");
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Theme Files/js/selectTheDate.js");
</script>

هر چیزی رو می‌تونید با تابع includeLanguageScript فراخونی کنید. توی کد بالا من غیر از چیزایی که توی این پروژه نیاز داشتم، فایل جاوااسکریپتی به اسم selectTheDate فراخونی کردم که قراره توش به روش JSOM کد بنویسم؛ پایین‌تر توی همین فایل کنترل، داخل تگ ul کد HTML پاپ‌آپی رو نوشتم که با فشار دادن یه دکمه قراره باز بشه و اطلاعاتی رو از کاربر دریافت کنه؛ چون از این پاپ‌آپ هم فقط یه دونه توی صفحه نیاز داشتم، این کد رو هم توی فایل کنترل می‌نویسم:

<ul class="cbs-List">

     <!-- pop -->
     <div id="popUpTerms" class="popUpKDM">
          <div class="white popUpContent pa1">

               <!-- date -->
               <div id="dateSelection" class="mt1">
                    <label for="date">انتخاب تاریخ</label>
                    <input type="text" class="date" placeholder="لطفاً انتخاب کنید" />
               </div>
               <!-- date -->

               <!-- actions -->
               <div class="popUpClosed">لغو</div>
               <div class="popUpAccept">ثبت</div>
               <!-- actions -->

          </div>
     </div>
     <!-- pop -->

توی سناریویی که برای نوشتن این مقاله انتخاب کردم، کاربر قراره با فشار دادن یه دکمه، یه پاپ‌آپی رو باز کنه و یه تاریخی رو انتخاب کنه، بعد ما انتخاب اون کاربر رو توی یه لیستِ شیرپوینتی که قبلاً ساختیم ذخیره کنیم؛ برای همین توی کد بالا توی پاپ‌آپ، یه فیلد گذاشتم برای انتخاب تاریخ شمسی.

همون طور که گفتم برای باز شدن پاپ‌آپ کاربر باید یه دکمه‌ای رو فشار بده، اگه فقط یک دکمه برای باز کردن پاپ‌آپ نیاز داشتیم می‌تونستیم همین جا توی همین فایل مارک‌آپ دکمه رو هم بنویسیم، اما من می‌خوام این دکمه به ازای هر آیتمی که سرچ برمی‌گردونه تکرار بشه، برای همینم مارک‌آپِ دکمه رو توی فایل آیتم می‌نویسم:

<a class="btn numGenerator" href="">انتخاب یک تاریخ</a>

خب حالا که خیالم از بابت مارک‌آپ این پاپ‌آپ راحت شد، می‌تونم برم سراغ فایل selectTheDate.js که قراره کار اصلی توش انجام بشه؛ اما قبل از اون باید یاد بگیریم اصلاً JSOM نوشتن چطوریه؟

JSOM در فایل جاوااسکریپت

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

$(document).ready(function() {
     function stuffToRunAfterSP_JS() {
          console.log('sp.js is loaded now!')
     }
     ExecuteOrDelayUntilScriptLoaded(stuffToRunAfterSP_JS, "sp.js")
})

کاری که این کد انجام می‌ده اینه که صبر می‌کنه تا فایل sp.js توی صفحه کاملاً بارگذاری بشه و بعد تابع stuffToRunAfterSP_JS رو اجرا می‌کنه. حالا همه چی آماده‌س و می‌تونیم شروع کنیم به نوشتن JSOM.

ساختار توابع JSOM در شیرپوینت

اساساً هر تکه مجزا از کد JSOM که توی هر پروژه نوشته می‌شه و عملیات خاصی رو انجام می‌ده شامل این پنج تا بخش اصلیه:

  • کانتکست ـ آدرس سایت هدف
  • لیست ـ عنوان یا شناسه لیست، در صورت نیاز
  • کوئری ـ کمل کوئری برای دریافت آیتم‌ها، در صورت نیاز
  • عملیات ـ مثلاً حذف یا اضافه یا ویرایش کردن یک آیتم
  • توابع ـ شامل دو تابع که بعد از موفقیت یا شکست عملیات، مراحل بعدی رو مشخص می‌کنن

دریافت اطلاعات با JSOM در شیرپوینت

با یه مثال خیلی ساده همه چی خیلی واضح‌تر می‌شه: فرض کنیم قراره عنوان همه آیتم‌هایی از یه لیست رو که توشون کلمه «فروش» هست فراخونی کنیم و توی کنسول لاگ کنیم؛ برای این کار باید یه تابع بنویسیم:

$(document).ready(function() {
     function stuffToRunAfterSP_JS() {
          getSaleItems()
     }
     ExecuteOrDelayUntilScriptLoaded(stuffToRunAfterSP_JS, "sp.js")
})

var codingListItems
function getSaleItems() {

        // 1 - ctx
        var clientContext = new SP.ClientContext('https://portal.yourwebsite.com/subsite')

        // 2 - list
        var codingList = clientContext.get_web().get_lists().getByTitle('CodingUnits')

        // 3 - query
        var queryCodingList = new SP.CamlQuery()
        queryCodingList.set_viewXml("<View><Query><Where><Contains><FieldRef Name='Title'/><Value Type='Text'>فروش</Value></Contains></Where></Query></View>")
        codingListItems = codingList.getItems(queryCodingList)
        clientContext.load(codingListItems)

        // 4 - functions
        clientContext.executeQueryAsync(Function.createDelegate(this, onQueryOneSucceededs), Function.createDelegate(this, onQueryOneFaileds))

}
function onQueryOneSucceededs() {
     var codingListEnumerator = codingListItems.getEnumerator()
     var resultCount = codingListItems.get_count()
     if(resultCount > 0) {
          while (codingListEnumerator.moveNext()) {
               var theTitle = codingListEnumerator.get_current().get_item("Title")
               console.log(theTitle)
          }
     } else {
          alert('موردی که شامل کلمه فروش باشد وجود ندارد!')
     }
}
function onQueryOneFaileds(sender, args) {
     alert('Request failed. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace())
}

همون طور که توی کد بالا می‌بینید، تابع اصلی getSaleItems شامل چهار بخشه که من شماره‌گذاری کردم.

بخش اول، کانتکست، در واقع آدرس سایتیه که دارید اطلاعات رو ازش می‌گیرید. اگه سایت مورد نظرتون همون سایتیه که کدتون داره توش اجرا می‌شه، دیگه لازم نیست آدرس سایت رو به عنوان پارامتر توی متد ClientContext وارد کنید.

بخش دوم اسم لیست مورد نظرتونه. من توی این مثال از عنوان لیست استفاده کردم، اما JSOM توابع دیگه‌ای هم برای مشخص کردن لیست هدف داره که مثلاً از شناسه لیست استفاده می‌کنن.

بخش سوم کمل‌کوئریه که روی لیست اعمال شده تا فقط آیتم‌هایی از لیست که توی عنوانشون کلمه «فروش» هست به ما نمایش داده بشه. دقت کنید که توی کمل‌کوئری وقتی داریم از یه ستون شیرپوینتی استفاده می‌کنیم، باید از internal name اون ستون استفاده کنیم. internal name اون اسمیه که اولین بار موقع ساختن یه ستون وارد کردید و هرگز قابل تغییر نیست (اون اسمی که قابل تغییره display name اون ستونه).

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

ساختن آیتم با JSOM در شیرپوینت

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

var enDate

$(document).ready(function() {
     function stuffToRunAfterSP_JS() {

          // jalali date field
          $("#popUpTerms input.date").pDatepicker({

               // date format
               format: 'YYYY/MM/DD',
               initialValue: false,

               // set year
               onSelect: function(unix){

                    // the selected date
                    var date = new Date(unix).toLocaleDateString('fa-IR')

                    // the english date
                    enDate = new Date(unix)

            }

        })
          
          // show pop up
          $('a.numGenerator').on('click', function(event) {

               //show pop up
               $('#popUpTerms').show()
            
               // actions
               $('#popUpTerms .popUpAccept').show();
               $('#popUpTerms .popUpAccept').on('click', function(){
                    // create the item
               })
               $('.popUpClosed').show();
               $('.popUpClosed').on('click', function(){
                    $('#popUpTerms').hide();
               })

        })

     }
     ExecuteOrDelayUntilScriptLoaded(stuffToRunAfterSP_JS, "sp.js")
})

توی کد بالا وقتی کاربر پاپ‌آپ رو باز کنه و تاریخ رو انتخاب کنه، تاریخ انتخاب‌شده توی متغیر enDate قرار می‌گیره؛ جلوتر می‌بینید که برای مشخص کردن تاریخی که قراره توی آیتم جدید ذخیره کنیم از همین متغیر استفاده می‌کنیم. یه خرده استایل هم اضافه کردم که پاپ‌آپ از نظر ظاهری قابل‌قبول باشه:

/* popup */
.popUpKDM {
     display: none;
     background: #00000070;
     position: fixed;
     width: 100vw;
     height: 100vh;
     right: 0;
     top: 0;
     z-index: 999;
}
.popUpContent {
     overflow: hidden;
     width: max-content;
     min-width: 25%;
     left: 0;
     right: 0;
     top: 50%;
     transform: translate(0px, -50%);
     margin: auto;
     position: absolute;
     height: max-content;
     font-size: 1.25em;
     border-radius: .5em;
}
.popUpAccept, .popUpCancel, .popUpClosed {    
     background: #a2ffa2;
     padding-left: 1em;
     padding-right: 1em;
     width: max-content;
     float: left;
     margin-top: 1em;
     border-radius: .25em;
     height: 2.5em;
     line-height: 2.5em;
     cursor: pointer;
} 
.popUpClosed {    
     background: #ffdda8;
}
.popUpCancel {
     background: #ffcaca;
     margin-left: .5em;
}
/* popup */
JSOM در شیرپوینت

بعد از انتخاب تاریخ و فشار دادن دکمه ثبت، باید تابع JSOM فراخونی بشه و یه آیتم توی لیستی که ما بهش می‌گیم ثبت کنه. تابع JSOM رو بر اساس ساختاری که بالاتر توضیح دادم می‌نویسیم:

function createItem() {

     // 1 - ctx
     var clientContext = new SP.ClientContext('https://portal.yourwebsite.com/subsite')

     // 2 - list
     var oList = clientContext.get_web().get_lists().getByTitle("The List Name")

     // 3 - create
     var oListItemCreationInformation = new SP.ListItemCreationInformation()
     var oListItem = oList.addItem(oListItemCreationInformation)
     oListItem.set_item('Title', 'تست ثبت تاریخ')
     oListItem.set_item('SPSDate', enDate)
     oListItem.update()
     clientContext.load(oListItem)
        
     // 4 - functions
     clientContext.executeQueryAsync(andicatorOnSuccess, andicatorOnFail)

}
function andicatorOnSuccess() {
     alert('یک آیتم جدید ساخته شد')
}
function andicatorOnFail(sender, args) {
     alert('خطا در مرحله ساختن مورد در اندیکاتور')
     alert('Failed' + args.get_message() + '\n' + args.get_stackTrace());
}

این تابع هم مثل تابع قبلی شامل چهار بخشه که شماره‌گذاری کردم.

بخش اول که آدرس سایته. بخش دوم اسم لیستیه که آیتم قراره توش ساخته بشه. توی این تابع کمل‌کوئری نداریم و بخش سوم شامل متدهای مربوط به ساختن آیتم جدیده؛ مثل کمل‌کوئری، این جا هم وقتی قراره از یه ستون شیرپوینتی نام ببریم، باید از internal name اون ستون استفاده کنیم. بخش چهارم هم شامل دو تا تابعه که به ترتیب در صورت موفقیت و شکست عملیات، مشخص می‌کنن که قدم‌های بعدی چی قراره باشه.

این دو تا مثال ساده از نحوه کار کردن JSOM توی شیرپوینت بود. در نهایت کدی که برای اضافه کردن یه آیتم به لیست استفاده می‌شه اینه:

var enDate

$(document).ready(function() {
     function stuffToRunAfterSP_JS() {

          // jalali date field
          $("#popUpTerms input.date").pDatepicker({

               // date format
               format: 'YYYY/MM/DD',
               initialValue: false,

               // set year
               onSelect: function(unix){

                    // the selected date
                    var date = new Date(unix).toLocaleDateString('fa-IR')

                    // the english date
                    enDate = new Date(unix)

            }

        })
          
          // show pop up
          $('a.numGenerator').on('click', function(event) {

               //show pop up
               $('#popUpTerms').show()
            
               // actions
               $('#popUpTerms .popUpAccept').show();
               $('#popUpTerms .popUpAccept').on('click', function(){
                    createItem()
               })
               $('.popUpClosed').show();
               $('.popUpClosed').on('click', function(){
                    $('#popUpTerms').hide();
               })

        })

     }
     ExecuteOrDelayUntilScriptLoaded(stuffToRunAfterSP_JS, "sp.js")
})

function createItem() {

     // 1 - ctx
     var clientContext = new SP.ClientContext('https://portal.yourwebsite.com/subsite')

     // 2 - list
     var oList = clientContext.get_web().get_lists().getByTitle("RecAndicator")

     // 3 - create
     var oListItemCreationInformation = new SP.ListItemCreationInformation()
     var oListItem = oList.addItem(oListItemCreationInformation)
     oListItem.set_item('Title', 'تست ثبت تاریخ')
     oListItem.set_item('SPSDate', enDate)
     oListItem.update()
     clientContext.load(oListItem)
        
     // 4 - functions
     clientContext.executeQueryAsync(andicatorOnSuccess, andicatorOnFail)

}
function andicatorOnSuccess() {
     alert('یک آیتم جدید ساخته شد')
}
function andicatorOnFail(sender, args) {
     alert('خطا در مرحله ساختن مورد در اندیکاتور')
     alert('Failed' + args.get_message() + '\n' + args.get_stackTrace());
}

API اینستاگرام ـ یک راهنمای کامل

Basic Display API، مهم‌ترین API اینستاگرام، به توسعه‌دهنده اجازه می‌ده به اطلاعات پروفایل، تصاویر و ویدیوهای پست شده توی حسابِ کاربرِ اینستاگرام، با اجازه خودِ اون کاربر دسترسی داشته باشه؛ حدس می‌زنم این همون چیزیه که ویجت‌ها و افزونه‌های نمایش فید اینستاگرام توی وردپرس ازش استفاده می‌کنن. هیچ منبع فارسی خوبی پیدا نکردم که توضیح داده باشه چطوری می‌شه به Basic Display API متصل شد و باهاش کار کرد، پس سعی کردم توی این مقاله خیلی ساده و سریع توضیح بدم خودم چطوری انجامش دادم. اما قبل از هر چیز ممکنه براتون سؤال پیش بیاد که:

وقتی این همه کتابخونه آماده هست، چرا API اینستاگرام؟

من با هر کسی درباره گرفتن اطلاعات یه کاربر از API اینستاگرام صحبت کردم و توضیح دادم دارم از چه روشی استفاده می‌کنم، ازم پرسید چرا از یکی از هزاران کتابخونه‌ای که توی گیت‌هاب هست و برای همین کار توسعه داده شده استفاده نکردم؟ واقعیتش اینه که اتفاقاً منم اول همین کارو کردم و از یه کتابخونه node.js استفاده کردم که اتفاقاً توی گیت‌هاب خیلی پُرستاره هم بود. خوش‌حال و سرخوش بودم چون می‌تونستم اطلاعات همه کاربرا رو بدون هیچ محدودیت و اجازه‌ای کرال کنم؛ اما بعد از مدتی استفاده از اون کتابخونه متوجه شدم که دیگه کار نمی‌کنه. فکر کردم حتماً من یه جایی دارم اشتباه می‌کنم، اما با دنبال کردن تگ Instagram توی استک‌اورفلو فهمیدم خیلی از کسایی که داشتن از چنین کتابخونه‌هایی استفاده می‌کردن یه‌دفعه به مشکل خوردن؛ با داشتن این تجربه و یه خرده تحقیق و جست‌وجوی دیگه، متوجه شدم که بهترین و مطمئن‌ترین راهکار برای به دست آوردن اطلاعات حساب اینستای یه کاربر، استفاده از Basic Display API و خوندن داکیومنتای خودِ اینستاگرام و فیس‌بوکه.

حالا Basic Display API به چه دردی می‌خوره؟

از Basic Display API می‌شه برای دسترسی به هر نوع حساب اینستاگرامی استفاده کرد، اما همین الان باید بهتون بگم که این دسترسی فقط در حد خوندن اطلاعاته و تازه توی این روش، همین دسترسی هم حتماً با اجازه و رضایت صاحب حساب اینستاگرام انجام می‌شه. مثلاً یه استفاده خیلی ساده‌ش اینه که فید آخرین پست‌های صفحه اینستاگرامتونو توی سایت‌تون نمایش بدید. اگه دنبال این هستید که برنامه‌ای بنویسید که به طور نامحدود به اطلاعات حساب‌های اینستاگرامی دسترسی پیدا کنه یا مثلاً محتوا توی اینستاگرام پست کنه، این API به درد کار شما نمی‌خوره. به طور کلی می‌تونید از Basic Display API این توقعات رو داشته باشید:

  • اتصال به حساب کاربر برای گرفتن توکن دسترسی یا همون Access Token
  • دسترسی به اطلاعات پایه‌ای پروفایل کاربر مثل نام کاربری و تعداد پست‌ها
  • دسترسی به تصاویر، ویدیوها و آلبوم‌های کاربر

اینا هم چند تا از محدودیت‌های این API:

  • دسترسی به استوری‌ها، ریل‌ها و نظرات وجود نداره
  • دسترسی به پست‌های تبلیعاتی (پروموت) وجود نداره
  • توکن دسترسی کوتاه‌مدت بعد از یه ساعت منقضی می‌شه
  • توکن دسترسی بلندمدت بعد از دو ماه منقضی می‌شه

چه کارهایی قراره انجام بدیم؟

به طور خلاصه اگه بخوام بگم، یه اپلیکیشن باید توی فیس‌بوک بسازید، بعد از کسب رضایت از کاربر اپلیکیشنتون یه توکن دسترسی بلندمدت از API اینستاگرام بگیرید تا بتونید درخواست‌های دیگه‌ای رو به API اینستاگرام ارسال کنید، یه روشی در نظر بگیرید برای ذخیره توکن و در نهایت تصمیم بگیرید که داده‌های کاربرو چطور نمایش بدید و باهاش چی کار بکنید؛ می‌دونم ممکنه یه خرده پیچیده به نظر برسه، ولی وقتی یه بار این کارارو انجام بدید می‌بینید که چقدر همه چی با عقل جور درمیاد و منطقیه. قراره این کارها رو بکنیم:

  • ساختن یه اپلیکیشن در فیس‌بوک
  • دریافت اجازه اتصال به حساب کاربری در اینستاگرام
  • دریافت توکن دسترسی کوتاه‌مدت
  • تبدیل توکن دسترسی کوتاه‌مدت به توکن دسترسی بلندمدت
  • استفاده از توکن دسترسی بلندمدت برای اتصال به API
  • نمایش همه تصاویر و ویدیوهای حساب اینستاگرامِ کاربر
  • گرفتن تأییدیه نهایی

فرض من اینه که شما یه آشنایی مختصری با PHP دارید، چون من درخواست‌های cURL رو با PHP ارسال کردم (البته اگه به جای PHP دوست دارید از فناوری دیگه‌ای استفاده کنید هم هیچ مشکلی نیست، می‌تونید برای ارسال درخواست‌ها از ابزارهای دیگه‌ای مثل پست‌من استفاده کنید)؛ همچنین فرض کردم که شما یک حساب توسعه‌دهنده فیس‌بوک دارید (اگه ندارید یکی بسازید، ساختنش مجانی و ساده‌س اما اگه به آموزش نیاز داشتید یه نگاهی به این صفحه بندازید)، یک حساب اینستاگرام برای تست کردن دارید که توی چیزای مختلفی پست کرده باشید و یه وبسایت یا صفحه گیت‌هاب دارید که بتونید کدهاتونو توش تست کنید (دیدم که بعضیا روی لوکال هم این کارو انجام دادن، اما خودم امتحان نکردم هنوز). راستی توی این پروژه به دفعات باید به حساب توسعه‌دهنده فیس‌بوکتون مراجعه کنید، پس اگه توی ایران زندگی می‌کنید طبیعتاً به یه فیلترشکن معمولی هم نیاز دارید؛ اگه این پیش‌نیازها رو دارید و آماده هستید، شروع کنیم.

ساختن یک اپلیکیشن در فیس‌بوک

اول از همه به آدرس https://developers.facebook.com مراجعه کنید. اگه توی حسابتون وارد شده بودید روی My Apps کلیک کنید، یه اپ جدید بسازید و نوعش رو Consumer انتخاب کنید.

API اینستاگرام

بعد از ساختن اپ، از منوی سمت چپ روی Settings و بعد Basic کلیک کنید. آخرِ صفحه جدیدی که باز شد، روی Add platform کلیک کنید، گزینه وبسایت رو انتخاب کنید و آدرس وبسایتتونو وارد کنید. البته بعداً اگه خواستید می‌تونید پلتفرم رو تغییر بدید و هر چیزی رو انتخاب کنید، اما من چون توسعه‌دهنده وب هستم، پلتفرم وبسایت برام مناسبه و این مقاله هم بر اساس همین پلتفرم نوشته شده؛ توی همین صفحه‌ای که هستید فیلدهای Privacy Policy URL و User data deletion هم پیدا کنید و فعلاً همون آدرس سایتتون رو توشون وارد کنید؛ بعداً اگه لازم شد می‌تونید این فیلدها رو تغییر بدید. در نهایت تغییرات رو ذخیره کنید.

بعد که تغییرات ذخیره شدن، از منوی سمت چپ، روی Add Product کلیک کنید و Instagram Basic Display رو پیدا کنید. روی Set up کلیک کنید و توی صفحه جدید، روی دکمه Create New App کلیک کنید و یک برنامه جدید بسازید.

توی صفحه جدید، سه تا فیلد هست که باید یه آدرسی از سایتتونو توشون وارد کنید؛ مهم نیست اگه آدرس‌ها تکراری بودن، آدرس‌ها رو وارد کنید و بعد تغییراتو ذخیره کنید. بعد از ذخیره تغییرات، مقداری که توی فیلد Valid OAuth Redirect URIs ذخیره شده رو به صورت کامل کپی کنید و جایی نگه‌داری کنید؛ همچنین مقادیری که توی فیلدهای Instagram App ID و Instagram App Secret وجود دارن هم یه جایی کپی کنید؛ بعداً به این سه تا فیلد نیاز داریم.

خب تا این جا تونستید یه اپلیکیشن بسازید، اما چون اپلیکیشنی که الان ساختید توی حالت توسعه‌س و هنوز لایو نیست، باید مشخص کنید که کدوم حساب‌های اینستاگرامی می‌تونن از این اپلیکیشن استفاده کنن؛ برای این کار باید این مراحل رو انجام بدید:

  • از منوی سمت چپ روی Roles و بعد دوباره روی Roles کلیک کنید
  • توی صفحه جدید روی Add Instagram Testers کلیک کنید
  • توی پاپ‌آپی که باز می‌شه نام کاربری حساب اینستاگرامتونو وارد کنید
  • با فشار دادن دکمه Submit دعوت‌نامه رو ارسال کنید
  • یک تب جدید باز کنید و وارد حساب اینستاگرامی که براش دعوت‌نامه رو ارسال کردید بشید
  • از نوار بالا، روی عکس پروفایلتون کلیک کنید و دکمه Profile رو بزنید
  • حالا روی آیکون چرخ‌دنده کلیک کنید و Apps and Websites رو بزنید
  • توی صفحه جدید روی تب Tester Invites کلیک کنید و Accept رو بزنید

دریافت اجازه اتصال به حساب کاربری در اینستاگرام

اون طور که من متوجه شدم، اگه قرار باشه یه اپلیکیشن تحت وب به اطلاعاتِ حسابِ اینستاگرامِ یک کاربر متصل بشه، یه چنین فرایندی باید طی بشه:

  • یه دکمه توی سایت باشه که روش مثلاً نوشته باشه «اتصال به اینستاگرام»
  • کاربر روی دکمه کلیک می‌کنه و منتقل می‌شه به پنجره صدور مجوز دسترسی
  • کاربر به اپلیکیشن شما اجازه می‌ده که به حساب اینستاگرامش متصل بشید
  • اینستاگرام، کاربرو هدایت می‌کنه به آدرسی که قبلاً به اپلیکیشن‌تون معرفی کردید (توی مرحله قبل) و همچنین یک کد مجوز برای شما صادر می‌کنه
  • اپلیکیشنِ شما از کدِ مجوز استفاده می‌کنه تا از API، توکنِ دسترسی دریافت کنه

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

https://api.instagram.com/oauth/authorize
  ?client_id={app-id}
  &redirect_uri={redirect-uri}
  &scope=user_profile,user_media
  &response_type=code

توی آدرس بالا به جای {app-id} باید شناسه اپلیکیشنی که توی فیس‌بوک ساختیدو بذارید و به جای {redirect-url} هم آدرسی که توی مرحله قبل توی فیلد Valid OAuth Redirect URIs وارد کردیدو قرار بدید. بعد یه مرورگر باز کنید و آدرس نهایی رو توش وارد کنید. اگه همه چی خوب پیش رفته باشه باید پنجره صدور مجوزو ببینید. بعد از این که دکمه آبی رو زدید و دسترسی‌ها رو پذیرفتید، می‌بینید که اینستاگرام شما رو به آدرسی که قبلاً وارد کرده بودید هدایت می‌کنه، در حالی که پارامتر code هم به اون آدرس اضافه شده؛ این کد، همون کد مجوزیه که برای دریافتِ توکن دسترسیِ کوتاه‌مدت بهش نیاز داریم؛ دقت کنید که این کد فقط یک ساعت اعتبار داره.

مثلاً آدرسی که من بهش هدایت شدم این بود:

https://kdesigns.ir/instaghetti/?code=AQDcRp_TKPilQbozi-c09oRkypvy50zoLmDLgghd2U_TvxjFpy3s2HeLfc26oEZAbtKrQKlenRoAVtgkez9Msm3zFD12JD0T1PU2kFfbqW2-ZvEvoWtpqrHhlrlKYgUdeGjaD6321YaTYZJPy0oWKp1r4TcyBu4Qno5wPUleDgNOb_H4YomWRcTlmSselI8GYxJhkRK8cyTjiMaiM5vv1u-6N2bY0po1DagW2FrP2epdow#_

می‌بینید که به آدرسی که من به اپلیکیشن داده بودم یه پارامتر code اضافه شده. توی پروژه من، برای این که کاربرا راحت بتونن وصل شن به حساب اینستاگرامشون من یه لینک توی صفحه گذاشتم که کاربرو هدایت می‌کنه به صحفه صدور مجوز؛ شما توی پروژه خودتون ممکنه برای هدایت کاربر به صفحه صدور مجوز روش دیگه‌ای رو انتخاب کنید؛ بنابراین این بخش بستگی داره به پروژه شما. به هر حال من از چنین کدی استفاده کردم که یه لینک ساده‌س:

<a href="https://api.instagram.com/oauth/authorize?client_id={app-id}&redirect_uri=https://kdesigns.ir/instaghetti&scope=user_profile,user_media&response_type=code">
     <span>برای اتصال به اینستاگرام کلیک کنید</span>
</a>

دریافت توکن دسترسی کوتاه‌مدت

حالا باید کد مجوز رو تبدیل کنیم به توکن دسترسی کوتاه‌مدت؛ من برای این کار از PHP استفاده کردم، اما همون طوری که قبلاً توضیح دادم، شما می‌تونید با هر زبونی که راحت هستید یه در خواست cURL برای API ارسال کنید یا حتی از پست‌من یا ابزارهایی شبیه به پست‌من استفاده کنید. به هر حال باید یک درخواست POST ارسال کنید که این ویژگی‌ها رو داشته باشه:

curl -X POST \
     https://api.instagram.com/oauth/access_token \
          -F client_id={app-id} \
          -F client_secret={app-secret} \
          -F grant_type=authorization_code \
          -F redirect_uri={redirect-uri} \
          -F code={code}

به جای {app-id}، {app-secret} و {redirect-uri} باید اطلاعات مربوط به اپلیکیشن خودتون که توی سایت فیس‌بوک ساختید رو وارد کنید و به جای {code} هم باید از کد مجوز استفاده کنید. بعد از ارسال درخواست، اینستاگرام به شما یه آبجکت JSON برمی‌گردونه که شامل توکن دسترسیِ کوتاه‌مدته. یه چیزی شبیه به این:

{
     "access_token": "IGQVJ...",
     "user_id": 17841405793187218
}

من کد دسترسیِ کوتاه‌مدت رو داخل متغیر access_token ذخیره کردم، چون بعداً قراره بهش نیاز پیدا کنم. شما هم اگه از PHP استفاده می‌کنید، می‌تونید از این کد برای ارسال درخواستتون استفاده کنید. دقت کنید که به جای {app-id} و {app-secret} و {redirect_uri} باید مشخصات اپلیکیشن خودتونو قرار بدید.

$curl = curl_init();
curl_setopt_array($curl, array(
     CURLOPT_URL => 'https://api.instagram.com/oauth/access_token',
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_ENCODING => '',
     CURLOPT_MAXREDIRS => 10,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_FOLLOWLOCATION => true,
     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
     CURLOPT_CUSTOMREQUEST => 'POST',
     CURLOPT_POSTFIELDS => 'client_id={app-id}&client_secret={app-secret}&grant_type=authorization_code&redirect_uri={redirect_uri}&code=' . $_GET['code'],
     CURLOPT_HTTPHEADER => array(
          'Content-Type: application/x-www-form-urlencoded',
          'Cookie: csrftoken=pyJBucmiv1VPdbZlIRphThte2Uli0BZP; ig_did=4357FE06-12AF-4455-B69B-A0EC0899C20D; ig_nrcb=1; mid=Yo6JeQAEAAFMOZx3tGujp0u11XI3'
     ),
));
$response_access_token = curl_exec($curl);
$json_array = json_decode($response_access_token, true);
$key = "access_token";
$access_token = $json_array[$key];

تبدیل توکن دسترسی کوتاه‌مدت به توکن دسترسی بلندمدت

به صورت پیش‌فرض، توکن‌های دسترسی اینستاگرام کوتاه‌مدت هستند و فقط برای یک ساعت اعتبار دارن؛ درسته که این توکن الان کار ما رو راه می‌ندازه، اما چون مدت اعتبارش کمه، باید تبدیلش کنیم به توکن دسترسی بلندمدت که دو ماه اعتبار دارن؛ برای این کار باید یک درخواست GET ارسال کنیم که این ویژگی‌ها رو داشته باشه:

curl -i -X GET "https://graph.instagram.com/access_token
     ?grant_type=ig_exchange_token
     &client_secret={app-secret}
     &access_token={short-lived-token}"

دوباره به جای {app-secret} باید اطلاعات مربوط به اپلیکیشن خودتون رو وارد کنید و به جای {short-lived-token} هم باید از توکن کوتاه‌مدت استفاده کنید. بعد از ارسال درخواست، اینستاگرام به شما یه آبجکت JSON برمی‌گردونه که شاملِ توکنِ دسترسیِ بلندمدته. یه چیزی شبیه به این:

{
     "access_token":"{long-lived-user-access-token}",
     "token_type": "bearer",
     "expires_in": 5183944  // Number of seconds until token expires
}

اگه از PHP استفاده می‌کنید، می‌تونید از کدِ زیر برای ارسال درخواستتون استفاده کنید. دقت کنید که به جای {app-secret} باید از مشخصات اپلیکیشن خودتون استفاده کنید. ضمناً همون طوری که می‌بینید من از توکنی که توی مرحله قبل به دست آوردم برای ارسال درخواست استفاده کردم.

$curl = curl_init();
curl_setopt_array($curl, array(
     CURLOPT_URL => 'https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_secret={app-secret}&access_token=' . $access_token,
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_ENCODING => '',
     CURLOPT_MAXREDIRS => 10,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_FOLLOWLOCATION => true,
     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
     CURLOPT_CUSTOMREQUEST => 'GET',
     CURLOPT_HTTPHEADER => array(
          'Cookie: csrftoken=pyJBucmiv1VPdbZlIRphThte2Uli0BZP; ig_did=4357FE06-12AF-4455-B69B-A0EC0899C20D; ig_nrcb=1; mid=Yo6JeQAEAAFMOZx3tGujp0u11XI3'
     ),
));
$response_long_token = curl_exec($curl);
$json_array_long = json_decode($response_long_token, true);
$key_long = "access_token";
$access_token_long = $json_array_long[$key_long];
update_field('long_lived_token', $access_token_long, 'user_' . get_current_user_id());
curl_close($curl);

توی کد بالا، توکن دسترسی بلندمدت توی متغیر access_token_long ذخیره شده و همچنین با استفاده از تابع update_field توکن دسترسی بلندمدت توی یه فیلدی که مربوط به پروفایل کاربر توی سایت خودمه ذخیره شده؛ این طوری دیگه لازم نیست هر دفعه که کاربر وارد حسابش می‌شه دوباره همه این مراحلو طی کنه و توکن دسترسی دریافت کنه.

استفاده از توکن دسترسی بلندمدت برای اتصال به API

بعد از این همه دردسر برای به دست آوردن توکن دسترسیِ بلندمدت، بالاخره می‌تونیم اطلاعات کاربرو از API درخواست کنیم. برای دسترسی به اطلاعات کاربر باید یک درخواست GET ارسال کنیم با این پارامترها:

GET https://graph.instagram.com/me?fields={fields}&access_token={access-token}

توی این مرحله از یه هدف کوچیک شروع می‌کنیم: من یه درخواست ارسال می‌کنم تا نام کاربری حساب رو از API کوئری کنم. از توکنی که مرحله قبل به دست آوردم استفاده می‌کنم برای تکمیل درخواست:

$curl = curl_init();
curl_setopt_array($curl, array(
     CURLOPT_URL => 'https://graph.instagram.com/me?fields=username&access_token=' . get_field('long_lived_token', 'user_' . get_current_user_id()),
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_ENCODING => '',
     CURLOPT_MAXREDIRS => 10,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_FOLLOWLOCATION => true,
     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
     CURLOPT_CUSTOMREQUEST => 'GET',
     CURLOPT_HTTPHEADER => array(
          'Cookie: csrftoken=pyJBucmiv1VPdbZlIRphThte2Uli0BZP; ig_did=4357FE06-12AF-4455-B69B-A0EC0899C20D; ig_nrcb=1; mid=Yo6JeQAEAAFMOZx3tGujp0u11XI3'
     ),
));
$response_user_name = curl_exec($curl);
$json_array_user = json_decode($response_user_name, true);
$key_user_name = "username";
$user_name_final = $json_array_user[$key_user_name];
curl_close($curl);

بعد از اجرای کد بالا می‌تونید محتویات متغیر json_array_user رو بررسی کنید و ببینید API چه اطلاعاتی رو به شما برگردونده. من فقط می‌خوام نام کاربری رو توی سایتم نشون بدم برای همین جلوی پارامتر fields فقط نوشتم username و تمام. شما می‌تونید به غیر از username اطلاعات دیگه مثل media_count که تعداد پست‌های کاربره رو هم درخواست کنید و توی سایتتون، هر اطلاعاتی که خواستید نمایش بدید.

نمایش همه تصاویر و ویدیوهای حساب اینستاگرامِ کاربر

برای نمایش همه آیتمای حساب کاربر، یک درخواست GET ارسال می‌کنیم با این پارامترها:

GET https://graph.instagram.com/me/media?fields={fields}&access_token={access-token}

از کد زیر استفاده کردم و اطلاعاتی که نیاز داشتم توی پارامتر fields داخل آدرس نوشتم، از پارامتر limit استفاده کردم تا تعداد آیتمایی که می‌خوامو مشخص کنم و در نهایت همه اطلاعاتی که API بهم برگردونده رو توی متغیر json_user_data ذخیره کردم.

$curl = curl_init(); 
curl_setopt_array($curl, array(
     CURLOPT_URL => 'https://graph.instagram.com/me/media?fields=media_url,timestamp,thumbnail_url,media_type&limit=999&access_token=' . get_field('long_lived_token', 'user_' . get_current_user_id()),
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_ENCODING => '',
     CURLOPT_MAXREDIRS => 10,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_FOLLOWLOCATION => true,
     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
     CURLOPT_CUSTOMREQUEST => 'GET',
     CURLOPT_HTTPHEADER => array(
          'Cookie: csrftoken=pyJBucmiv1VPdbZlIRphThte2Uli0BZP; ig_did=4357FE06-12AF-4455-B69B-A0EC0899C20D; ig_nrcb=1; mid=Yo6JeQAEAAFMOZx3tGujp0u11XI3'
     ),
));
$response_user_data = curl_exec($curl);
$json_user_data = json_decode($response_user_data, true);
curl_close($curl); 

بعد، از متغیر json_user_data استفاده کردم و با یه لوپ، همه پست‌های حساب اینستاگرام کاربرو توی سایت خودم نمایش دادم:

<?php foreach($json_user_data['data'] as $item) { ?>
     <?php if($item['media_type'] != 'CAROUSEL_ALBUM') { ?>
          <div class="col s12 m6 l3 mb1">
               <div class="polaroidContainer">
                    <a href="<?php echo $item['media_url']; ?>" target="_blank">

                         <!-- thumbnail -->
                         <div class="pRelative">
                              <?php if($item['thumbnail_url']) { ?>
                                   <img class="instaImageThumbnail" src="<?php echo $item['thumbnail_url']; ?>">
                                   <i class="material-icons medium primaryColor instagramPostIcon">play_arrow</i>
                              <?php } else { ?>
                                   <img class="instaImageThumbnail" src="<?php echo $item['media_url']; ?>">
                                   <i class="material-icons medium primaryColor instagramPostIcon">image</i>
                              <?php } ?>
                         </div>
                         <!-- thumbnail -->

                         <!-- download -->
                         <?php if($item['media_type'] == 'IMAGE') { ?>
                              <p class="primaryColor fontW600 instaPostCaption ma0">تصویر بزرگ</p>
                         <?php } elseif ($item['media_type'] == 'VIDEO') { ?>
                              <p class="primaryColor fontW600 instaPostCaption ma0">ویدیوی کامل</p>
                         <?php } ?>
                         <!-- download -->
                                
                         <!-- date -->
                         <p class="secondaryColor instaPostCaption theTimeStamp ma0"><?php echo $item['timestamp']; ?></p>
                         <!-- date -->

                    </a>
               </div>
          </div>
     <?php } else {
          $curl = curl_init(); 
          curl_setopt_array($curl, array(
               CURLOPT_URL => 'https://graph.instagram.com/' . $item['id'] . '/children?fields=media_url,timestamp,thumbnail_url,media_type&limit=999&access_token=' . get_field('long_lived_token', 'user_' . get_current_user_id()),
               CURLOPT_RETURNTRANSFER => true,
               CURLOPT_ENCODING => '',
               CURLOPT_MAXREDIRS => 10,
               CURLOPT_TIMEOUT => 0,
               CURLOPT_FOLLOWLOCATION => true,
               CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
               CURLOPT_CUSTOMREQUEST => 'GET',
               CURLOPT_HTTPHEADER => array(
                    'Cookie: csrftoken=pyJBucmiv1VPdbZlIRphThte2Uli0BZP; ig_did=4357FE06-12AF-4455-B69B-A0EC0899C20D; ig_nrcb=1; mid=Yo6JeQAEAAFMOZx3tGujp0u11XI3'
               ),
          ));
          $response_child_data = curl_exec($curl);
          $json_child_data = json_decode($response_child_data, true);
          curl_close($curl); 
     ?>
     <?php foreach($json_child_data['data'] as $album_item) { ?>
          <div class="col s12 m6 l3 mb1">
               <div class="polaroidContainer">
                    <a href="<?php echo $item['media_url']; ?>" target="_blank">

                         <!-- thumbnail -->
                         <div class="pRelative">
                              <?php if($album_item['thumbnail_url']) { ?>
                                   <img class="instaImageThumbnail" src="<?php echo $album_item['thumbnail_url']; ?>">
                                   <i class="material-icons medium primaryColor instagramPostIcon">play_arrow</i>
                              <?php } else { ?>
                                   <img class="instaImageThumbnail" src="<?php echo $album_item['media_url']; ?>">
                                   <i class="material-icons medium primaryColor instagramPostIcon">image</i>
                              <?php } ?>
                         </div>
                         <!-- thumbnail -->

                         <!-- download -->
                         <?php if($album_item['media_type'] == 'IMAGE') { ?>
                              <p class="primaryColor fontW600 instaPostCaption ma0">تصویر بزرگ</p>
                         <?php } elseif ($album_item['media_type'] == 'VIDEO') { ?>
                              <p class="primaryColor fontW600 instaPostCaption ma0">>ویدیوی کامل</p>
                         <?php } ?>
                         <!-- download -->

                         <!-- date -->
                         <p class="secondaryColor instaPostCaption theTimeStamp ma0"><?php echo $album_item['timestamp']; ?></p>
                         <!-- date -->

                    </a>
               </div>
          </div>
          <?php } ?>
     <?php } ?>
<?php } ?>

دقت کنید که توی کد بالا چون بعضی از پست‌ها ممکنه آلبوم باشن، مجبور شدم به ازای هر آلبوم یه درخواست cURL دیگه هم به API ارسال کنم و اطلاعات اون آلبوم هم نمایش بدم.

خب تموم شد! می‌دونم یه خرده طولانی شد، ولی فک کنم مهم‌ترین مرحله این بود که توکنِ دسترسیِ طولانی‌مدت رو به دست بیارید. امیدوارم این اطلاعات و مثال‌ها به دردتون خورده باشه. منبع اصلی نوشتن این مقاله خود سایت فیس‌بوک بوده که شما هم اگه خواستید با این API کار کنید حتماً باید با دقت داکیومنت‌هاش رو مطالعه کنید. برای تولید کدهای PHP هم از برنامه پست‌من کمک گرفتم.

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