Deel je artikel op Facebook

Ingediend door Dirk Hornstra op 16-mar-2019 00:30

Vorig jaar heb ik een artikel gedeeld over de API van LinkedIn en mijn koppeling (link). Wegens misbruik en dergelijke zijn de restricties op de Facebook API aangescherpt. Hierdoor werkt de module die ik op mijn persoonlijke website ( www.durkotheek.nl ) heb niet meer. Ook voor de andere apps krijg ik waarschuwingen binnen dat ik zaken moet controleren. Je moet dan allemaal zaken invullen en ter beoordeling insturen. Wat een gedoe. Zo zijn API's volgens mij niet bedoeld.

Maar goed, in mijn Wordpress-site deelde ik automatisch aanbiedingen op mijn Facebookpagina (link) en nu ik over ben naar Umbraco zou dat ook weer zo moeten gaan. Dus dit maal zelf in .NET een koppeling met de Facebook-API maken.

We gaan eerst naar https://developers.facebook.com/. Ik ga hier naar mijn APP, heb je die nog niet, dan kun je die hier aanmaken. Bovenin de pagina zie je jouw App ID. Onder Settings, Advanced zie je het Client-Token. Het gaat me om het posten naar een pagina, overzicht van de API-calls staat hier: https://developers.facebook.com/docs/pages/publishing

Je moet eerst een access-token hebben. Ik ga hiervoor eerst naar de Graph API Explorer: https://developers.facebook.com/tools/explorer/
Bij Application kies ik voor mijn App. Bij user of page voor de pagina. Er zijn 5 permissies aangevinkt, user_friends, business_management, manage_pages, pages_show_list en publish_pages. Klik onderaan op Get Acces Token en kopieer de waarde die je daar krijgt.

Vervolgens zou je het ID van je Facebookpagina moeten hebben. Dat kon vroeger door de URL van de profielfoto van de pagina te bekijken, https://www.facebook.com/paginanaam/photos/a.[1e numerieke waardes]/[2e numerieke waardes]/?type=1&theater. Waarbij je dan de 1e of 2e numerieke waarde zou moeten hebben. Maar dat werkt niet meer zo, ik vermoed dat het een security-issue was en Facebook het daarom aangepast heeft. Met onderstaande code krijg ik wel het ID te zien en nog beter, ook een access-token:


            string accessToken = "[kopieer die uit de facebook-graph API explorer]";
            using (WebClient wc = new WebClient())
            {
                string myAccount = UTF8Encoding.UTF8.GetString( wc.DownloadData($"https://graph.facebook.com/v3.2/me?access_token={accessToken}"));
                Console.WriteLine(myAccount);
                string myPages = UTF8Encoding.UTF8.GetString(wc.DownloadData($"https://graph.facebook.com/v3.2/me/accounts?access_token={accessToken}"));
                Console.WriteLine(myPages);
            }

Dat access-token gebruik ik om een bericht te posten. En dat werkt! Let op, in de URL moet je dus de API-versie meenemen! Zo stond dit niet op de /docs/pages/publishing-pagina. Dit is op zondagavond, 24 februari. Ik houd dit nog even in de gaten omdat dit token misschien kan verlopen (expire), dus dan moet ik alsnog kijken hoe we dit "automatisch" gaan vernieuwen.


 

                string _ShareToken = "[hier het token plaatsen dat je hierboven in je myPages hebt staan.]";
                HttpWebRequest wr = (HttpWebRequest)WebRequest.Create("https://graph.facebook.com/v3.2/[id van mijn facebookpagina uit myPages]/feed");
                wr.AllowAutoRedirect = true;
                wr.KeepAlive = true;
                wr.ContentType = "application/x-www-form-urlencoded";

                wr.KeepAlive = true;
                wr.Method = "POST";
                wr.Expect = "";
                wr.ProtocolVersion = HttpVersion.Version10;
                wr.Accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg";
                wr.UserAgent = "Mozilla/5.0 (Windows NT 6.0; rv:9.0.1) Gecko/20100101 Firefox/9.0.1";
                wr.Headers.Add("Accept-Language", "en-us");
                wr.Headers.Add("Accept-Encoding", "gzip, deflate");
                wr.Headers.Add("Cache-Control", "no-cache");

                // post values
                StreamWriter writer = new StreamWriter(wr.GetRequestStream());
                string message = "dit is een testbericht";
                string link = "https://www....";
                writer.Write($"message={message}&link={link}&access_token={_ShareToken}");
                writer.Close();
                HttpWebResponse wresp = (HttpWebResponse)wr.GetResponse();
                StreamReader reader = new StreamReader(wresp.GetResponseStream());
                var r = reader.ReadToEnd();
                Console.WriteLine(r);
                reader.Close();

Tijdens het testen van de applicatie loop ik er al tegen aan dat het token inderdaad verlopen is.  Ok, de app kan dus posten, ik moet zorgen dat de OAuth-flow goed geïmplementeerd wordt. Eerst de informatie-pagina erbij: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/

Na allemaal pagina's doorgelopen te hebben, testcode ingevoerd te hebben, blijkt de oplossing toch simpeler. Het gaat tenslotte om een Access Token voor mijn eigen App(licatie). Als je ingelogd bent op Facebook en je gaat naar https://developers.facebook.com/tools/accesstoken/ dan krijg je daar een overzicht van Access Tokens van jouw apps. Klik op de knop Debug achter het User Token van jouw applicatie. Daar zie je dat dit token ongeveer na een uur verloopt. Maar onderaan staat een knop: Extend Access Token. Als je daarop klikt, komt eronder weer een regel. Klik ook daar op de knop Debug. Het AccessToken wat je hier ziet zegt bij Expires "Never", bij Data Expires "in about 3 months". Als dat betekent dat ik na 3 maanden dit token weer even zo moet vernieuwen, dan is dat geen enkel probleem! Na 4 dagen kan ik nog steeds een bericht op mijn Facebookpagina plaatsen, dus dit werkt.

Door het token in te vullen bij "[kopieer die uit de facebook-graph API explorer]" kun je in de myPages-json zien wat de naam van de pagina is waar je op wilt delen. Die heb je nodig om het juiste AccessToken op te halen. Als je die hebt, kun je onderstaande voorbeeldcode gebruiken, hiermee heb je het _AccessToken waarmee je vervolgens het bericht met bovenstaande code kunt plaatsen.


        const string _AccessToken = "[je extended access token]";
        const string _PageName = "[hierboven bepaald, doe maar ff lowercase]";
        string _ShareToken = "";
      
         private static void GetMyPages()
        {
            using (WebClient wc = new WebClient())
            {
                string myPages = UTF8Encoding.UTF8.GetString(wc.DownloadData($"https://graph.facebook.com/v3.2/me/accounts?access_token={_AccessToken}"));
                var pages = JsonConvert.DeserializeObject<FacebookPageContainer>(myPages);
                foreach (var page in pages.data)
                {
                    string pageName = page.name;
                    _ShareToken = page.access_token;
                    if (pageName.ToLower() == _PageName)
                    {
                        break;
                    }
                }
            }
        }

Dit werkt dus goed, dus het plaatsen van berichten op Pagina's gaat me nu wel lukken. Nu zit ik alleen nog met mijn eigen "wall" op Facebook. Als ik een artikel plaats op www.durkotheek.nl, dan deelde ik ook altijd dat bericht. 

Ik ben eerst maar eens naar de Graph Explorer gegaan: https://developers.facebook.com/tools/explorer/

De standaard functie die daar aangeroepen wordt is /me?fields=id,name
Die waardes zie je ook in het eerste stukje voorbeeld code bij myAccount.
Je krijgt daar jouw ID terug.

Dus ik probeer daar "naar toe" te posten. Maar dat lukt niet. Ik word verwezen naar https://developers.facebook.com/docs/graph-api/using-graph-api/#publishing

Hier staat dat je alleen naar een Page kunt publiceren, voor alle andere "publicaties", gebruik Sharing, dit linkt naar https://developers.facebook.com/docs/sharing

Het lijkt erop dat je vanuit de API niet meer naar je eigen timeline kunt schrijven.
Lezen kan wel (via /me/feed), maar daar gaat het me niet om. Dus kijken of ik een "share-button" kan toevoegen;

https://developers.facebook.com/docs/sharing/reference/share-dialog

Hier staat dat je eigenlijk alleen maar even een "linkje" hoeft toe te voegen:
https://www.facebook.com/dialog/share?app_id=[je app ID]&display=popup&href=[url te delen]

Op zich wel een mooie manier. Want ik zie namelijk de eerste keer geen afbeelding.
Open ik de URL nog een keer, dan komt er wel netjes een preview in beeld.
Ik moet nog wel even de og:-tags even goed instellen voor het stukje tekst wat erbij komt.

Ik heb een app mijn eigen naam gegeven, dus je ziet ook dat het artikel gedeeld is "via" Dirk Hornstra.

Mijn eigen website draait op Wordpress. Als je onderstaande code opslaat als "facebook_share_action.php" in je wp-content/plugins map kun je in Wordpress in het Admin-deel onder Plugins deze activeren. Daarna heb je bij het bewerken van een artikel naast de Publish/Update knop een linkje "Deel op Facebook".


<?php
/**
 * Facebook Share Post Link
 * 2019-03-16 Dirk Hornstra
 * 
 * Plugin Name: Facebook Share Post Link
 * Description: Shows a "share on Facebook link" which opens a dialog
 * Author:      Dirk Hornstra
 * Version:     0.0.1
 * Author URI:  https://www.durkotheek.nl/
 */

add_action('post_submitbox_start', 'facebook_share_action');

function facebook_share_action() {
        global $post;

        if (is_object($post)) {
                facebook_share_render_post_link();
        }
}

function facebook_share_render_post_link() {
?>
<div><a href="https://www.facebook.com/dialog/share?app_id=[jouw app-ID]&display=popup&href=<?php echo get_permalink($post->ID);?>" target="_blank">Deel op Facebook</a></div>
<?php
}
?>

Dat werkt goed, alleen zat ik nog even met de tekst die onder het afbeelding op Facebook komt. In mijn geval was dat: "naam auteur | categorie artikel". Ik wilde daar een deel van de tekst van het artikel hebben. Door het toevoegen van een eigen functie in functions.php van je thema en het aanpassen van je header.php heb ik dat  ook werkend gekregen:


// functions.php

 

function custom_get_description($postID) {
    $pageForDescription = get_page($postID);
    return substr(wp_strip_all_tags($pageForDescription->post_content, true), 0, 200)."...";
}

// header.php

<?php $overrule = false;

if (!(is_category()) && !(is_home())) {
    if (have_posts()) {
        $overrule = true;
    }
}
if ($overrule == false) {
?>
<meta name="og:description" content="<?php echo get_bloginfo( 'description'); ?>">
<?php
}
else {
?>
<meta name="og:description" content="<?php echo custom_get_description( get_the_ID()); ?>">
<?php
}   

?>