转换Facebook的涂鸦墙讯息到开放式XML试算表

Facebook的串流(stream)是一个存取你Facebook涂鸦墙上讯息的简单方法,并且可以用程式来做很好的使用,它跟Twitter 的feed有点类似。

这个范例是一个应用程式的范例,用来拉出串流然后使用PHPExcel将它放进一个格式良好的开放式xml试算表,这个试算表重新安排讯息为一个时间的纪录每一列有人的照片、他们的名字(有一个连结到个人档案)、讯息的内容以及发布的日期时间。

[adsense]

隐私权的考虑

在串流内的资料来自你的朋友那边;他给你权限来看他们的涂鸦墙但却未必有意让大众这样做,因为这样,以及Facebook的商业模式运作的方式,会有严格的认证要求;这个要求比twitter feed还严,因此这范例不适合给你权限在我的实作中 – 你需要建构自己的应用范例!

步骤1:注册Facebook应用程式

要开发Facebook的应用程式你需要在你的Facebook页面http://facebook.com/developers 先建立新的应用程式,然后按‘建立新的应用程式’按钮并给它一个名称,这会给你一组金钥:API金钥跟应用程式密钥,这个稍后在你的程式码中会用到。

此处的说明http://developers.facebook.com/get_started.php对设定有很大的帮忙,当然你也可以看ㄚ琪的中文说明Facebook PHP教学

到你的应用程式设定页面,从http://www.facebook.com/developers/ => 在‘我的应用程式’ => 点击你应用程式的名称 => 点击 ‘编辑设定’ => 点击’Facebook集成’=>‘Canvas’

勾选‘Canvas Type’的Iframe按钮,然后在‘Canvas Page’填上应用程式名称以及在’Canvas URL’填上你的应用程式网址,这样可以让canvas转向到你的页面去,允许你透过Facebook的应用程式页例如http://apps.facebook.com/reallysnazzyapp/到你的应用程式页面来存取你的应用程式。

步骤2:托管你的应用程式

当你在Facebook注册应用程式的时候,它看起来像是Facebook的一部分,可是你需要自己托管程式码 – 这个程式码会放在这个公用网站让Facebook可以显露这个程式。

有很多免费的php主机供应商你可以试试看,有些不错有些就很难用,你会需要php5.2的版本并且开放–enable-zip使用(这样PHPExcel函式库才能运作)

你可以提出一个页面用下面的指令码来检查这些细节:

<?php

echo phpinfo();

这将显示你的PHP安装的设定资讯。

假如你有这个资源,我建议有你自己的web伺服器来开发,透过这些方法控制环境会让开发程序不会那么复杂。

步骤3:撰写应用程式

这支应用程式不只在Facebook上显示,程式码还会存取Facebook的资料,对于一个存取Facebook资料的应用程式来说安全设定是需要的。

为了验证Facebook,你需要更改标志及session金钥;这个就是你的API金钥跟应用程式密钥(你在步骤1产生的),这个部份大多数是由PHP Facebook API函式库来处理:http://svn.facebook.com/svnroot/platform/clients/packages/facebook-platform.tar.gz(注:这个连结已移除,取而代之的是下面的连结,但是因为函式库已经有变更,新的函式库不支援这里的程式,所以你只能从这里的程式码使用旧的函式库)

https://github.com/facebook/php-sdk

程式码有四个部份:

· Facebook的验证

· 从Facebook拉出资料

· 透过资料来处理/检索

· 新增资料到有时尚格式的试算表中

Facebook的验证

在你的程式码中要include Facebook API函式库:

include_once ‘../php/facebook.php’;

接着增加程式码来提供验证。

你会需要用你自己的金钥、应用程式密钥来替代,以及验证后要转向的网址,或是使用者要取消:

$appapikey = “xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’;

$appsecret = ‘yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy’;

$next = “http://SomeServer.com/spike/index.php”; //Where to redirect after authentication

$next_cancel = “http://SomeServer.com/spike/canceled.php”; // Where to redirect if the user cancels the authentication

//—–Login, authenticate and get permission to read the user’s stream—–

$facebook = new Facebook($appapikey, $appsecret);//Create a new facebook object

//Sometimes the session token has expired or is invalid, this throws an execption. If this happens catch it and reset the user and try again

try

{

$user_id = $facebook->require_login();//get the user to login

if (!$facebook->api_client->users_hasAppPermission(‘read_stream’,$user_id))

{

$facebook->redirect(“http://www.facebook.com/authorize.php?api_key=” . $appapikey . “&v=1.0&ext_perm=read_stream&next=” . $next . “&next_cancel=” . $next_cancel );

}

}

catch (Exception $ex)

{

//If the session token has expired or is invalid, this will effectivly reset it and then redirect to try again.

$facebook->set_user(null, null);

$facebook->redirect($_SERVER[‘SCRIPT_URI’]);

exit;

}

这段程式码需要使用者登录并确认他们是否有这应用程式的权限可以读取他们的涂鸦墙讯息,假如没有,会转向到授权页,你需要新增一个位址来让使用者授权后可以转到哪去,以及他们想要去取消可以到哪,DARCY THOMAS选择转回目前的页面这个最简单的方案,他也准备好了一个普通简单的页面让使用者在他们要取消时可以转移到那里。

canceled.php :

<?php

echo “No love?”;

有时候你会发现你应用程式的session金钥会过期,这就是为什么要用try catch,假如有问题,程式会捕捉到然后重新设定,并让使用者重试。

在下载档案之前显示欢迎的讯息

一旦认证完成,我们要确认是否第一次载入,假如是我们就给使用者一个讯息说选择下载到试算表,点击submit按钮会让资料拉出来,并且转换到试算表中。

//the first time the user arives at this page $_REQUEST[‘mode’] will != 1 so just display the intro

// if the user clicks the submit then $_REQUEST[‘mode’] == 1 and the file will be created and presented to the user

if ($_REQUEST[‘mode’] != 1)

{

echo ‘<form action=”index.php” method=”get”>’;

echo ‘Download posts from your wall, as an Open XML Spreadsheet <br/>’;

echo ‘<input type=”hidden” name=”mode” value=”1″/><input type=”submit” /></form>’;

}

else

{

// …create file and present to the user

这是必须的因为假如你转向到另一页,这个session金钥会改变然后认证会失败,对于更强大的解决方案,你可以用switch case来判断mode的值以含括不同的PHP档。

从Facebook拉下资料:

拉下结果相当地容易

$feed = $facebook->api_client->stream_get();

这预设会拉下你朋友最新的30条涂鸦墙讯息。

$feed 是‘posts’、‘profiles’及‘albums’的巢状阵列。

在范例中,DARCY THOMAS只有使用‘posts’跟‘profiles’两个栏位。

你也可以藉着设定一些参数来扩充stream_get() 传回的结果

$facebook->api_client->stream_get(viewer_id, source_ids, start_time, end_time, limit, filter_key);

http://developers.facebook.com/docs/reference/rest/stream.get/有更多的资讯。

处理/搜寻资料

接下来在放资讯到阵列之前我们使用这资讯流($feed)来撷取我们想要的资讯,我们可以使用:

// get the revelent infomation out the stream ($feed) and put into an array

$posts = $feed[‘posts’];

$profiles = $feed[‘profiles’];

//Put in the names for the row headings

$listMessageDetails = array

(

“0”=>array

(

“image”=>”Image”,

“name”=>”Name”,

“message”=>”Message”,

“time”=>”Time created”

)

);

// Put in the rest of the details for each message

$i =1;

foreach ($posts as $post)

{

$message = $post[‘message’];

//If the post is auto generated by some other Facebook app (i.e., xyz quiz), it normally doesnt have a message

//so we dont add the details of these posts

if( !empty($message))

{

$userId = $post[‘actor_id’];

$created_time = $post[‘created_time’]; // this is in’unix time’ format

$details = getDetails($userId, $profiles);

$image = $details[‘pic_square’];

$name = $details[‘name’];

$url = $details[‘url’];

$MessageDetails[‘name’] = $name;

$MessageDetails[‘message’] = $message;

$MessageDetails[‘time’] = date(“g:i a, F j, Y”, $created_time ); // convert ‘unix time’ to 5:16 pm, March 10, 2001

$MessageDetails[‘id’] = $userId;

$MessageDetails[‘image’] = $image;

$MessageDetails[‘url’] = $url;

$listMessageDetails[$i] = $MessageDetails;

$i++;

}

}

在‘post’阵列中的资料有很多的资讯,其中大部份跟我们在这应用程式中感兴趣的不相干(例如‘post_id’、‘permalink’等等),这会拉出我们感兴趣的资料并放进阵列中,(谁说过什么、他们的资讯以及他们说什么)。

一个post只有post的user id(例如 actor_id)而不是他们的名字,连结到他们的个人资讯等等,所以getDetails()函式会从资讯流($feed)的‘profile’阵列中拉出资料。

试算表的栏位也需要一些标题这样就能容易地在这时候放进这个阵列。

使用样式格式将资料放入试算表

透过PHPExcel函式库你可以使用处理过的资料并放进试算表中然后应用一些格式,你会需要从http://phpexcel.codeplex.com/ (注有可能你无法下载,很幸运地这个范例也这函式库也有了)下载PHPExcel函式库,放这个函式库资料在伺服器上然后在你的指令码的最前面含括起来。

include ‘PHPExcel/IOFactory.php’;

PHPExcel的文件很有用而且有很多的例子;最好确认你有没有看过。

接着建立一个新的PHPExcel物件然后载入资料。

//—–Take the data in $listMessageDetails and put into a spreadsheet using the PHPExcel library

$objPHPExcel = new PHPExcel(); //create an new PHPExcel spreadsheet object

$sheet = $objPHPExcel->getActiveSheet();

foreach ($listMessageDetails as $row => $MessageDetails)

{

//put the values from each message into a new row, in the correct columns

//note: columns are 0-based indexed but rows are 1-based. So ‘A1’ and an index of (0,1) would refer to the same cell.

$sheet->getColumnDimension(‘A’)->setWidth(12);

$sheet->getColumnDimension(‘B’)->setWidth(20);

$sheet->getCellByColumnAndRow(1 , $row + 1)->setValueExplicit($MessageDetails[‘name’], PHPExcel_Cell_DataType::TYPE_STRING);

$sheet->getColumnDimension(‘C’)->setWidth(100);

$sheet->getCellByColumnAndRow(2 , $row + 1)->setValueExplicit($MessageDetails[‘message’], PHPExcel_Cell_DataType::TYPE_STRING);

$sheet->getStyle(‘C’ . $row)->getAlignment()->setWrapText(true);

$sheet->getColumnDimension(‘D’)->setWidth(20);

$sheet->getCellByColumnAndRow(3 , $row + 1)->setValueExplicit($MessageDetails[‘time’], PHPExcel_Cell_DataType::TYPE_STRING);

}

这段程式码会使用$listMessageDetails阵列的所有东西然后载入到$objPHPExcel准备好格式化。

这是设定栏位宽度很方便的地方,所以同时可以这样做,这里有一个问题你需要注意,就是:栏位是以0为基础做索引而列是以1为基础,所以‘A1’跟(0,1)的索引都是参考相同的储存格。

大部分这里使用的格式都是从较早DARCY THOMAS在http://openxmldeveloper.org/archive/2009/05/06/4606.aspx 所写的的PHP/Open XML 文章中复制来的,参考这篇文章有关如何使用PHPExcel函式库格式化更多的资讯,这里所使用的很多部份都是不用说明就可以懂的;所以DARCY THOMAS在这里没有做太多的说明。

有一些基本的版面配置属性:

//Set Print properties

$objPHPExcel->getActiveSheet()->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); //Set printing orentation

// set autofilter on the columns

$objPHPExcel->getActiveSheet()->setAutoFilter(‘A1:’ . $objPHPExcel->getActiveSheet()->getHighestColumn() . $objPHPExcel->getActiveSheet()->getHighestRow() );

Make the headings bold, put in some borders and alternating row colors for visual clarity:

//—–Put in some formatting to the table data to make it easer to read—–

$highestRow = $objPHPExcel->getActiveSheet()->getHighestRow();

$highestColumn = $objPHPExcel->getActiveSheet()->getHighestColumn();

$objPHPExcel->getActiveSheet()->insertNewRowBefore($highestRow + 1, 1);//Add one more row as a footer to the table

//put a border on the top and bottom rows and make the title row bold

$objPHPExcel->getActiveSheet()->getStyle(‘A1’)->getFont()->setBold(true);

$objPHPExcel->getActiveSheet()->getStyle(‘A1’)->getBorders()->getTop()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);

$objPHPExcel->getActiveSheet()->getStyle(‘A’ . ($highestRow + 1) )->getBorders()->getBottom()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);

//Loop through all of the rows and put in fill and borders on the edges

for($row =1; $row<$highestRow + 2; $row++) //remember that

{

//Set the colors, mid blue/grey for the top and bottom rows, with alternating white and light blue/grey

if ($row == 1 || $row ==$highestRow + 1) $color = ‘FFCFDAE7’;

else if ($row%2==0) $color = ‘FFFFFFFF’;

else $color = ‘FFE7EDF5’;

// set the fill type and apply the color

$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row)->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);

$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row)->getFill()->getStartColor()->setARGB($color);

//duplcate the first cells style (fill plus the top and bottom borders) across the whole row

$objPHPExcel->getActiveSheet()->duplicateStyle( $objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row), ‘B’ . $row . ‘:’. $highestColumn . $row); //copy style set in first column to the rest of the row

//Put some borders on the far left and right cells of the row

$objPHPExcel->getActiveSheet()->getStyle(‘A’ . $row )->getBorders()->getLeft()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);

$objPHPExcel->getActiveSheet()->getStyle($highestColumn . $row )->getBorders()->getRight()->setBorderStyle(PHPExcel_Style_Border::BORDER_THIN);

}

藉着撷取影像将个人档案的大头贴照放进试算表中然后存到一个暂存档中,接着使用PHPExcel将大头贴照插入试算表中:

//—–put the profile image into the streadsheet in column ‘A’ —–

$highestRow = $objPHPExcel->getActiveSheet()->getHighestRow();

for($row =1; $row<$highestRow; $row++)

{

//set the row height to 50 for all but the first row

if ($row !=1) $objPHPExcel->getActiveSheet()->getRowDimension($row)->setRowHeight(50);

$objDrawing = new PHPExcel_Worksheet_Drawing();

$objDrawing->setName(‘Profile Image’);

$objDrawing->setDescription(‘Profile Image’);

$pathURL = $listMessageDetails[$row 1][‘image’];

$path = tempnam(sys_get_temp_dir() , ‘path’ ); //creates a temp file to put the image in

if( !empty($pathURL) && $row != 1) // if the image is inaccessable or doesnt exist (e.g., the first row) then skip

{

copy($pathURL, $path); // Copy the image from its url location to the temporary file, ready to be loaded in the the spreadsheet

$objDrawing->setPath($path);// the path of where to find the image to insert

$objDrawing->setHeight(50); //size you want the image to be displayed as

$objDrawing->setWorksheet($objPHPExcel->getActiveSheet());

$objDrawing->setCoordinates(‘A’ . $row);// set where you want to put the image

}

}

 

现在我们有每个讯息作者的Facebook个人档案的url,我们可以让他们的名字有一个可点击的超连结:

// Put in a link on the profile name back to the users Facebook page

for($row =2; $row<$highestRow ; $row++)

{

$url = $listMessageDetails[$row 1][‘url’];

$objPHPExcel->getActiveSheet()->getCell(‘B’ . $row)->getHyperlink()->setUrl($url);

}

 

最后,我们可以将试算表的档案传给使用者,要建立档案给浏览器你会需要含括表头:

header(‘Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’);

header(‘Content-Disposition: attachment;filename=”myfile.xlsx”‘);

header(‘Cache-Control: max-age=0’);

现在我们可以输出串流,这会用我们使用到的PHPExcel物件然后在写到输出前放到一个writer:

$objWriter2007 = PHPExcel_IOFactory::createWriter($objPHPExcel, ‘Excel2007’);

$objWriter2007->save(‘php://output’);

你可以拥有它了,从Facebook认证、取用资料,转换资料然后用格式化的试算表来表现。

完整的PHP应用程式附在这FacebookStreamToOpenXMLSpreadsheet.zip,好好享受吧!