Запрашивать разрешения facebook только при необходимости

У меня есть следующий скрипт, который работает, т.е. он переходит на страницу входа в facebook, если пользователь еще не вошел в систему, и спрашивает их, согласны ли они с приложением для публикации сообщений на своей стене:

<?php
    require 'facebook.php';

    $facebook = new Facebook(array(
        'appId'  => 'removed for security reasons',
        'secret' => 'removed for security reasons',
        'cookie' => true,
    ));

    $session = $facebook->getSession();

    if ($session) {

        if (isset($_GET[id])) {

            $post = $facebook->api("/" . $_GET['id'] . "/feed", "POST",  array('message' => 'Hello!'));
            echo 'A message has been posted on your friends wall';

        } else {

            $friends = $facebook->api('/me/friends');

            foreach ($friends as $key=>$value) {
                echo 'You have ' . count($value) . ' friends<br />';

                foreach ($value as $fkey=>$fvalue) {
                    echo 'friend id = ' . $fvalue[id] . ' - friend name = ' . $fvalue[name] . ' - <a href="/stage2.php?id=' . $fvalue[id] . '">post message</a><br />';
                }
            }
        }

    } else {

        $loginUrl = $facebook->getLoginUrl(array(
            'req_perms' => 'publish_stream',
            'next' => 'http://'.$_SERVER['SERVER_NAME'].'/stage1.php',
            'cancel_url' => 'http://'.$_SERVER['SERVER_NAME'].'/cancel.php',
        ));

        header('Location: '.$loginUrl);
    }
?>

Как это можно улучшить, чтобы при запуске не запрашивались расширенные разрешения. Он должен запрашивать только базовые разрешения для отображения списка друзей и запрашивать расширенные разрешения только в том случае, если пользователь нажимает на друга, чтобы опубликовать сообщение.


person oshirowanen    schedule 29.04.2011    source источник
comment
Насколько я могу судить, вы не запрашиваете publish_stream на этапе 1. Когда вы дойдете до этапа 2, будет действующий сеанс, а перенаправление никогда не будет выполнено.   -  person DannyKK    schedule 29.04.2011
comment
Ну да, но код, который вы разместили, никогда не будет запрашивать разрешение на публикацию потока. Вы можете использовать FQL, чтобы узнать, есть ли у вашего приложения необходимые разрешения.   -  person DannyKK    schedule 29.04.2011


Ответы (2)


Вкратце, есть кое-что, на что я хочу обратить внимание в отношении следующего блока кода:

foreach ($friends as $key=>$value) {
    echo 'You have ' . count($value) . ' friends<br />';

    foreach ($value as $fkey=>$fvalue) {
        echo 'friend id = ' . $fvalue[id] . ' - friend name = ' . $fvalue[name] . ' - <a href="/stage2.php?id=' . $fvalue[id] . '">post message</a><br />';
    }
}

Ваш 1-й цикл foreach действительно вводит в заблуждение и вообще не является хорошей практикой. Graph API не слишком последователен в том, как он представляет данные, но причина, по которой вы выполняете foreach, заключается в том, чтобы иметь дело с ключом data в возвращаемом объекте JSON. Как правило, это плохая идея, потому что ключ data обычно присутствует вместе с другими ключами (например, paging). Вместо этого я бы проверил, что $friends['data'] не пуст, а затем переназначил массив $friends следующим образом: $friends = $friends['data'];.

Пример:

if (!empty($friends['data']))
{
    $friends = $friends['data'];
}
else
{
    $friends = array();
}

теперь по вашему вопросу.

Вы упомянули, что не хотите чрезмерно запрашивать разрешения. Это здорово, но проблема в том, что Facebook не позволяет очень легко проверить, какие разрешения у вас есть, а какие нет. Существует таблица FQL, которая позволяет вам проверить, есть ли у вашего пользователя определенный набор разрешений, но эта таблица не обновляется с какой-либо срочностью. Если вы получаете дополнительные разрешения от пользователя (или если пользователь отзывает разрешения), а затем вы проверяете эту таблицу FQL на предмет статуса разрешения, она может (и, вероятно, будет) считать неверное значение, и вы получите ложное срабатывание.

У вас есть три варианта решения этой проблемы, о которых я сразу же подумал.

  1. Продолжайте свой код stage1.php, как и вы - нет ничего плохого в том, как вы получаете установку и сеанс для пользователя. Вы изменяете страницу 2, чтобы перенаправить пользователя через конечную точку OAuth, запрашивающую разрешение на публикацию потока каждый раз, когда пользователь загружает страницу. Конечная точка OAuth не будет повторно запрашивать у пользователя установку и отправит их дальше.

    Минусы такого подхода в том, что каждый запрос на публикацию на стене друзей превращается в 3 запроса.

    • The initial page load
    • Перенаправление/загрузка OAuth
    • Перенаправление с OAuth обратно в ваше приложение

    Этот подход также требует, чтобы вы добавили флаг к вашему ключу next в вашем loginURL, который вы можете искать, чтобы убедиться, что пользователь прошел через конечную точку OAuth, иначе вы получите бесконечную ошибку перенаправления.

  2. Используйте FB Javascript SDK, чтобы проверить текущий набор разрешений ваших пользователей. Для этого вы будете использовать метод FB.getLoginStatus.

    Пример:

    <div id="fb-root"></div>
    <script src="http://code.jquery.com/jquery-1.5.2.min.js"
        type="text/javascript" charset="utf-8">
     </script>
    <script src="http://connect.facebook.net/en_US/all.js"
        type="text/javascript" charset="utf-8">
     </script>
    <script type="text/javascript">
    (function($)
    {
        FB.init({
            appId: '<?= FB_APP_ID; ?>',
            cookie: true,
            status: true,
            xfbml: true
        });
    
        $('a').click(function(event)
        {
            var self = this;
    
            event.preventDefault();
    
            FB.getLoginStatus(function(session)
            {
                if (session.perms.match(/\"publish_stream\"/))
                {
                    /* This user has publish stream, so we don't need
                     * to ask again
                    **/
                    window.location = $(self).attr('href');
                }
                else
                {
                    /* This user does not have publish stream, so we need
                     * to ask.
                    **/
                    FB.login(function(response)
                    {
                        if (response && response.perms.match(/publish_stream/))
                        {
                            /* We now have publish stream access! */
                            window.location = $(self).attr('href');
                        }
                    }, {
                        perms: 'publish_stream'
                    });
                }
            })
    
            return false;
        })
    })(jQuery);
    
  3. Не используйте никаких расширенных разрешений, используйте Javascript SDK (снова) и предоставьте пользователю диалоговое окно публикации для каждого пользователя, которого они хотели бы опубликовать на стене. Это также относительно легко сделать.

    Пример:

    учитывая ваши ссылки для пользователей:

    <a href="#" data-id="123">Friend 1</a>
    <a href="#" data-id="456">Friend 2</a>
    <a href="#" data-id="789">Friend 3</a>
    

    Вы можете сделать что-то вроде этого:

    <div id="fb-root"></div>
    <script src="http://code.jquery.com/jquery-1.5.2.min.js"
        type="text/javascript" charset="utf-8">
    </script>
    <script src="http://connect.facebook.net/en_US/all.js"
        type="text/javascript" charset="utf-8">
    </script>
    <script type="text/javascript">
    (function($)
    {
        $('a').click(function(event)
        {
            var user_id = $(this).data('id');
    
            FB.ui({
                method:    'feed',
                message:   'Hello!',
                to:         user_id
            }, function(response)
            {
                //this gets called whether it was successful, or not.
            })
        });
    
    })(jQuery);
    
person Jim Rubenstein    schedule 29.04.2011
comment
Как бы вы переписали раздел цикла foreach? - person oshirowanen; 29.04.2011
comment
Вместо foreach($friends as $key=›$value) я бы просто сделал, if (!empty($friends)) { $friends = $friends['data']; } else { /* список друзей пуст */ } - person Jim Rubenstein; 29.04.2011

Вот переписанный вами код, который, как я думаю, является передовым:

<?php
require 'facebook.php';

$facebook = new Facebook(array(
    'appId'  => 'removed for security reasons',
    'secret' => 'removed for security reasons',
    'cookie' => true,
));

$session = $facebook->getSession();
// Prepare the login url with the right permission
$loginUrl = $facebook->getLoginUrl(array(
    'req_perms' => 'publish_stream',
    'next' => 'http://'.$_SERVER['SERVER_NAME'].'/stage1.php',
    'cancel_url' => 'http://'.$_SERVER['SERVER_NAME'].'/cancel.php',
));

if ($session) {
    try {
        // Before processing the request
        // check if we got the right permission
        $perms = $facebook->api(array(
            "method"    => "fql.query",
            "query"     => "SELECT publish_stream FROM permissions WHERE uid=me()"
        ));
        if($perms[0]['publish_stream']==='1') {
            // We have the right permission
            if (isset($_GET['id'])) {
                // A small security measure
                $id = (int) $_GET['id'];
                $post = $facebook->api("/$id/feed", "POST",  array('message' => 'Hello!'));
                echo 'A message has been posted on your friends wall';
            } else {
                $friends = $facebook->api(array(
                    "method"    => "fql.query",
                    "query"     => "SELECT uid,name FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=me())"
                ));
                foreach($friends as $friend)
                    echo "friend id = {$friend['uid']} - friend name = {$friend['name']} - <a href=\"/stage2.php?id={$friend['uid']}\">post message</a><br />";
            }
        } else {
            // We don't have the right permission
            header('Location: '.$loginUrl);
        }
    } catch (FacebookApiException $e) {
        error_log($e);
    }
} else {
    header('Location: '.$loginUrl);
}
?>

Как проверить разрешение объясняется здесь. Также я добавил комментарии, чтобы сохранить объяснение.

person ifaour    schedule 29.04.2011
comment
Запрос на разрешение publish_stream из таблицы разрешений FQL не обновляется с какой-либо (разумной) скоростью. Получение этого разрешения и его проверка в течение одного и того же 5-минутного окна не дает точного ответа в отношении того, какие разрешения предоставил вам ваш пользователь. Я проверил это только вчера, так как мне нужно было проверить разрешения пользователей. Кроме того, как он сказал выше, он не хочет запрашивать разрешения, которые ему не нужны (например, пользователь не обязательно хочет публиковать сообщения на стене своих друзей через свое приложение) - person Jim Rubenstein; 29.04.2011
comment
@JimR: Если я правильно понял его вопрос, этап 2 доступен только тогда, когда пользователь собирается опубликовать на стене своего друга. Итак, это, когда OP должен запросить это разрешение. - person ifaour; 30.04.2011
comment
Что касается таблицы permission, и может потребоваться время, чтобы проверить нашу проверку, это хороший момент. Я никогда не пробовал проверять скорость ответа, но я предполагаю, что для распространения на все серверы FB потребуется некоторое время. Если это так, то OP может не проверять разрешение и вместо этого перехватывать ошибку №. и если он соответствует тому, что у вас нет разрешения .. и т. д., ошибка №. тогда ==› спросите разрешения! - person ifaour; 30.04.2011