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

JSOM چیه؟

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

توی این مقاله من از یه وب پارت content search استفاده کردم؛ این کار خیلی منطقی‌ترین راه به نظر می‌رسه چون توی display templateهای پیش‌فرض شیرپوینت، روش‌هایی برای فراخونی فایل‌های CSS و JS وجود داره که جلوتر می‌بینید.

JSOM در content search

هر وب پارت 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 رو بر اساس ساختاری که بالاتر توضیح دادم می‌نویسیم:

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؟

من با هر کسی درباره گرفتن اطلاعات یه کاربر از اینستاگرام صحبت کردم و توضیح دادم دارم از چه روشی استفاده می‌کنم، ازم پرسید چرا از یکی از هزاران کتابخونه‌ای که توی گیت‌هاب هست و برای همین کار توسعه داده شده استفاده نکردم؟ واقعیتش اینه که اتفاقاً منم اول همین کارو کردم و از یه کتابخونه 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 انتخاب کنید.

بعد از ساختن اپ، از منوی سمت چپ روی 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 هم از برنامه پست‌من کمک گرفتم.

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

نادین سافت

سیکاس سپنتا

راهکار خلاق

آرمان پرتو تصویر

شرکت پلیرا

بال استودیو

روان‌شناس دانمارکی

گینز