Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Your question has two parts to it, one is storing the user preference and the other is share/unshare option to be given on the article page. The first part of the question is simple and can be achieved by simply having a small database table with minimum of two columns (for simplicity sake), userid (a varchar or long int) and share (a bool or a bit). Give an option to the user to toggle this share bit by giving an on/off button which changes the share value from 1 to 0 (true to false) and vice versa for a specified userid. Now before taking a social action (like read), check for this share bit in database for the logged facebook user and perform actions accordingly.</p> <p>Now to answer the second part of your question, you may use the Facebook JavaScript SDK to make api calls to news.read action and provide a callback to store the returned id of the shared article. Use the same id to then provide option of unshare for the user. Assuming that you have jQuery included in your page, something like below should work (I wrote and tested it in a jiffy, should work in most cases)</p> <blockquote> <p><strong>Include the below script in your page</strong></p> </blockquote> <pre><code>//set this at page load or from authResponse object var fbDataObj = { user_id : &lt;some user id&gt;, access_token: &lt;some access token&gt; }; //set currentPostFBUrl to your article url var currentPostFBUrl = "http://www.yourpageurl.com"; var publishPost = function (articleLink, callback) { FB.api( '/' + fbDataObj.user_id + '/news.reads', 'post', { article: articleLink, access_token: fbDataObj.access_token }, function(response) { if (!response || response.error) { //alert('Error occured'); if (typeof callback === 'function') { callback({text: "error", id: ""}); } } else { //alert('Share was successful! Action ID: ' + response.id); if (typeof callback === 'function') { callback({text: "success", id: response.id}); } } }); }; var deletePost = function (postId, callback) { FB.api( postId, 'delete', { access_token: fbDataObj.access_token }, function(response) { if (!response || response.error) { //alert('Error occured'); if (typeof callback === 'function') { callback({text: "error", id: ""}); } } else { //alert('Unshare was successful!'); if (typeof callback === 'function') { callback({text: "success", id: ""}); } } }); }; var publishOrDeleteArticle = function (btn_obj) { var btn_name = btn_obj.attr("name"); if (isNaN(parseInt(btn_name, 10))) { publishPost(currentPostFBUrl, function(status){ if (status.text === "success") { btn_obj.attr("name", status.id); btn_obj.text("Unshare"); } }); } else { deletePost(btn_name, function(status){ if (status.text === "success") { btn_obj.attr("name", "share"); btn_obj.text("Share") } }); } }; </code></pre> <blockquote> <p><strong>Now in your page do something like this</strong></p> <p><strong>Edit:</strong><br> (Also set currentPostFBUrl to your article url like below)<br> <em><strong>var currentPostFBUrl = "<a href="http://www.yourpageurl.com" rel="nofollow">http://www.yourpageurl.com</a>";</strong></em></p> </blockquote> <pre><code>//within script tag $(document).ready(function(){ $("#btn_share").click(function(e){ e.preventDefault(); publishOrDeleteArticle($(this)); }); }); //your actual share/unshare button &lt;a id="btn_share" name="share" href="#"&gt;Share&lt;/a&gt; </code></pre> <p><em>On the final note I have written one wrapper js class for one of the recent facebook application that I was working on. With this you can read/unread an article with just one line of code. There are other WordPress wrappers inside as well but that can be left alone in this case, at the simplest you may use the object as below after providing the initial configuration and init (check the attached code below). There might be a few bugs in the code and the methods might not be complete and extensive because I'm still working on it but for the time being it should solve the purpose. The below wrapper methods can also be used in the above mentioned code for clarity. Feel free to use/modify the code, give feedback and comments and also reply back in case any issues are identified.</em></p> <pre><code>/*! * -------------------------------------------------------------------------------------- * Utility Library for Facebook Application * Contains wrapper methods for WordPress JSON API (named WPJsonApi) * and for Facebook Javascript SDK (named FBJsWrapper) * Dependency : jQuery, Facebook JavaScript SDK * Author : Emad Alam * Date: Thu Jun 07 21:11:03 2012 +0530 * -------------------------------------------------------------------------------------- * Notes: * Including this script adds a global object called FBAppUtil to the window object. * You may initialize the object with init method, providing init time configurations. * Once FBAppUtil object is initted, you get two sub-ojects, WPJsonApi &amp; FBJsWrapper. * These two objects can be initted individually or while initing the main FBAppUtil. * Both the objects have a buffer that stores the last fetched data and responses. * Methods are provided to access these buffers individually. * Once the sub-objects are configured, their methods can be called from their references. * * Example Usage: * //main object init. config can be given at this time * FBAppUtil.init(); * * var wpJsonApiConfig = { * apiUrl : "http://www.example.com/wordpress/api", * permalinkEnabled : true, * crossDomain : true * }; * FBAppUtil.WPJsonApi.init(wpJsonApiConfig); * * // now you may use all the methods of FBAppUtil.WPJsonApi * FBAppUtil.WPJsonApi.getRecentPosts(someParams, someCallback); * FBAppUtil.WPJsonApi.getPost(someIdOrSlug, someCallback); * var data = FBAppUtil.WPJsonApi.lastFetched(); * var response = FBAppUtil.WPJsonApi.lastResponse(); * * // other facebook related scripts and sdks initializations * // at this point you should be having the FB object initialized * // you may pass the authResponse object to init the FBJsWrapper or * // populate one of your own to pass it to the FBJsWrapper.init(authResponse) * * var fbJsWrapperConfig = { * userID : &lt;logged in userId&gt;, * accessToken : &lt;user's accessToken&gt;, * signedRequest : &lt;from authResponse object&gt;, * expiresIn : &lt;from authResponse object&gt; * }; * FBAppUtil.FBJsWrapper.init(fbJsWrapperConfig); * * // now you may use all the methods of FBAppUtil.FBJsWrapper * FBAppUtil.FBJsWrapper.sendAppRequest("some message", someCallback); * FBAppUtil.FBJsWrapper.share(someArticleUrl, someCallback); * FBAppUtil.FBJsWrapper.unshare(someId, someCallback); * var fbdata = FBAppUtil.FBJsWrapper.dataFromLastCall(); * var fbresponse = FBAppUtil.FBJsWrapper.responseFromLastCall(); */ (function (window) { /** Local helper Buffer Class - Start **/ var LocalBuffer = function (size) { //enforce 'new' - object creation pattern if (!(this instanceof LocalBuffer)) { return new LocalBuffer(size); } //private variables var _buffer = { data : [], //data fetched from the last successfull call response : [] //response from the last call }, _size = (function (size){ var n = parseInt(size || 10, 10); return isNaN(n) ? 10 : n; }(size)); //default buffer size var _pushToBuffer = function (name, data) { if (typeof _buffer[name][_size-1] !== 'undefined') { _buffer[name].shift(); //remove the first element in case the buffer is full } _buffer[name].push(data); }; var _readFromBuffer = function (name) { var len = _buffer[name].length; return len === 0 ? {} : _buffer[name][len-1]; //read the last inserted without popping }; var _getDataFromBuffer = function () { return _readFromBuffer("data"); }; var _getResponseFromBuffer = function () { return _readFromBuffer("response"); }; //expose methods this.pushToBuffer = _pushToBuffer, this.readFromBuffer = _readFromBuffer, this.getDataFromBuffer = _getDataFromBuffer, this.getResponseFromBuffer = _getResponseFromBuffer }; /** Local helper Buffer Class - End **/ /** WordPress JSON API Plugin wrapper - Start **/ var WPJsonApi; (function () { var instance; WPJsonApi = function (config) { if (!(this instanceof WPJsonApi)) { return new WPJsonApi(config); } if (instance) { return instance; } instance = this; //config variables var apiUrl, //complete url for the api cross_domain, //jsonp cross domain calls permalink_enabled, //whether permalink enabled templates, //TODO: templating system buffer_size; //size of the buffer //private variables var _buffer; //the LocalBuffer object //form the final api url string for the json call var _getControllerUrl = function (controller_name) { var url = apiUrl; //base url if (!permalink_enabled) { url += "/?json=" + controller_name; if (cross_domain) { url += "&amp;callback=?"; } } else { url += "/" + controller_name; if (cross_domain) { url += "/?callback=?"; } } return url; }; //fetch posts using the jQuery getJSON //push data and response to buffer //on successfull fetch, return array of post objects to the callback var _getRecentPosts = function (paramObj, callback) { var url = _getControllerUrl("get_recent_posts"); //base url for the specified controller if (typeof paramObj === 'function') { callback = paramObj; //no parameters provided only callback paramObj = {}; } paramObj = paramObj || {}; $.getJSON(url, paramObj, function(data) { if (data.status === "ok") { _buffer.pushToBuffer("response", { status : "ok", success : "Successfully fetched the post for the specified id/slug." } ); _buffer.pushToBuffer("data", data); if (typeof callback === 'function') { callback(data.posts); } } else if (data.status === "error") { _buffer.pushToBuffer("response", { status: "error", error : data.error } ); } else { _buffer.pushToBuffer("response", { status: "error", error : "Unknown error!" } ); } } ); }; //fetch post by it's id or slug using the jQuery getJSON //push data and response to buffer //on successfull fetch, return the post object to the callback var _getPost = function (paramObj, callback) { var url = _getControllerUrl("get_post"), //base url for the specified controller id = parseInt(paramObj, 10); //assume the parameter to be id paramObj = paramObj || {}; if (typeof paramObj !== 'object') { if (typeof paramObj === 'number' || !isNaN(id)) { paramObj = {id : id}; } else if (typeof paramObj === 'string') { paramObj = {slug : paramObj}; } } if (isNaN(parseInt(paramObj.id, 10)) &amp;&amp; !paramObj.slug) { throw { status: "error", error : "Provide a valid id or slug to get a post." }; } //TODO: Avoid server hit by searching and returning the post // from the local buffer for the specified id/slug $.getJSON(url, paramObj, function(data) { if (data.status === "ok") { _buffer.pushToBuffer("response", { status : "ok", success : "Successfully fetched the post for the specified id/slug." } ); _buffer.pushToBuffer("data", data); if (typeof callback === 'function') { callback(data.post); } } else if (data.status === "error") { _buffer.pushToBuffer("response", { status: "error", error : data.error } ); } else { _buffer.pushToBuffer("response", { status: "error", error : "Unknown error!" } ); } } ); }; //initialize the object and add methods to it var _init = function (config) { if (typeof config === 'undefined') { throw { status: "error", error : "Provide a valid configuration object to initialize WPJsonApi." }; } apiUrl = config.apiUrl || "/api", //assume base url relative to current page cross_domain = config.crossDomain || false, //jsonp cross domain calls permalink_enabled = config.permalinkEnabled || true, //assume permalink enabled templates = config.templates || {}, //TODO: templating mechanisms buffer_size = config.bufferSize || 10, //assume buffer size to be 10 _buffer = new LocalBuffer(buffer_size); //new buffer object //expose the methods and variables this.getRecentPosts = _getRecentPosts; //method for fetching recent posts this.getPost = _getPost; //method to fetch the post by id or slug this.lastFetched = _buffer.getDataFromBuffer; //last fetched data from the buffer this.lastResponse = _buffer.getResponseFromBuffer; //response from the last roundtrip to server }; //init the object if config is provided while creating if (typeof config !== 'undefined') { _init(config); } //expose init this.init = _init; }; }()); /** WordPress JSON API Plugin wrapper - End **/ /** FB JavaScript SDK wrapper - Start **/ var FBJsWrapper; (function () { var instance; FBJsWrapper = function (config) { if (!(this instanceof FBJsWrapper)) { return new FBJsWrapper(config); } if (instance) { return instance; } instance = this; //config variables var access_token, //user access token expires_in, //time to expire signed_request, //the signed request object user_id; //user id of the current connected user //private variables var _buffer, //the LocalBuffer object _token_valid = true; //assume the access token to be valid var _isTokenValid = function () { //TODO: Implement the method to check for invalid access tokens or // invalid calls to FB APIs return _token_valid; }; var _read = function (article, callback) { //TODO: Avoid repeated code, make a generic function var paramObj = {}; //start with an empty parameter paramObj.article = article; //add article to the parameter object //if token is invalid, no further calls are possible, so return if (!_isTokenValid()) { //TODO: Provide a better way of handling this throw { status: "error", error : "Provide a valid configuration object to initialize FBJsWrapper." }; } if (!(!access_token || 0 === access_token.length)) { paramObj.access_token = access_token; //access token not empty, add it to the parameter object } //TODO: Make a generic function to handle this call FB.api( '/' + user_id + '/news.reads', 'post', paramObj, function(response) { var i, message, // response error message exists = false, //assume the words don't exist in the message probable_words = [ "session has expired", "session has been invalidated", "session is invalid", "has not authorized" ]; //list of words that may denote an invalid token //no response, return if (!response) { _buffer.pushToBuffer("response", { status : "error", error : "No response returned by the server!" } ); return; } //some error if (response.error) { message = response.error.message.toLowerCase(); //case insensetive match for (i in probable_words) { if (message.indexOf(probable_words[i]) &gt; -1) { exists = true; break; } } if (exists) { _token_valid = false; //denotes invalid token } _buffer.pushToBuffer("response", { status : "error", error : exists ? "Invalid access token!" : response.error.message } ); } else { _buffer.pushToBuffer("response", { status : "ok", success : "Successfully read the specified article." } ); _buffer.pushToBuffer("data", response); if (typeof callback === 'function') { callback(response.id); } } }); }; var _unread = function (articleId, callback) { //TODO: Avoid repeated code, make a generic function var paramObj = {}; //start with an empty parameter //if token is invalid, no further calls are possible, so return if (!_isTokenValid()) { //TODO: Provide a better way of handling this throw { status: "error", error : "Provide a valid configuration object to initialize FBJsWrapper." }; } if (!(!access_token || 0 === access_token.length)) { paramObj.access_token = access_token; //access token not empty, add it to the parameter object } //TODO: Make a generic function to handle this call FB.api( articleId, 'delete', paramObj, function(response) { var i, message, // response error message exists = false, //assume the words don't exist in the message probable_words = [ "session has expired", "session has been invalidated", "session is invalid", "has not authorized" ]; //list of words that may denote an invalid token //no response, return if (!response) { _buffer.pushToBuffer("response", { status : "error", error : "No response returned by the server!" } ); return; } //some error if (response.error) { message = response.error.message.toLowerCase();//case insensetive match for (i in probable_words) { if (message.indexOf(probable_words[i]) &gt; -1) { exists = true; break; } } if (exists) { _token_valid = false; //denotes invalid token } _buffer.pushToBuffer("response", { status : "error", error : exists ? "Invalid access token!" : response.error.message } ); } else { _buffer.pushToBuffer("response", { status : "ok", success : "Successfully unread the specified article." } ); _buffer.pushToBuffer("data", response); if (typeof callback === 'function') { callback(); } } }); }; var _sendAppRequest = function (message, callback) { var paramObj = {}; //start with an empty parameter if (typeof message === 'function') { //no message only callback provided callback = message; message = 'Invite friends to this app.'; } paramObj.method = 'apprequests'; paramObj.message = message.toString(); if (!(!access_token || 0 === access_token.length)) { paramObj.access_token = access_token; //access token not empty, add it to the parameter object paramObj.display = 'iframe'; //access token provided, iframe can be used } else { paramObj.display = 'popup'; //no access token present, use popup dialog } FB.ui(paramObj, function (request, to) { //TODO: Handle the error conditions _buffer.pushToBuffer("response", { status : "ok", success : "Successfully sent the app request." } ); _buffer.pushToBuffer("data", { request: request, to: to }); if (typeof callback === 'function') { callback(request, to); } }); }; var _init = function (config) { if (typeof config === 'undefined') { throw { status: "error", error : "Provide a valid configuration object to initialize FBJsWrapper." }; } access_token = config.accessToken || "", //assume a blank access token, will try to call FB.api without it expires_in = config.expiresIn || 0, //jsonp cross domain calls signed_request = config.signedRequest || {}, //signed request parameter user_id = config.userID || 'me', //assume 'me' (for a user it's user_id but for pages and apps it might be something else) buffer_size = config.bufferSize || 10, //default buffer size _buffer = new LocalBuffer(buffer_size); //local buffer object //expose the methods and variables this.sendAppRequest = _sendAppRequest; //method for sending the app request from a dialog this.share = _read; //method to read an article, news.read this.unshare = _unread //method to unread a previously read article this.dataFromLastCall = _buffer.getDataFromBuffer; //last fetched data from the buffer this.responseFromLastCall = _buffer.getResponseFromBuffer; //response from the last roundtrip to server }; //init the object if config is provided while creating if (typeof config !== 'undefined') { _init(config); } //expose init this.init = _init; }; }()); /** FB JavaScript SDK wrapper - End **/ /** The main Utility Class - Start **/ var FBAppUtil; (function () { var instance; FBAppUtil = function () { if (!(this instanceof FBAppUtil)) { return new FBAppUtil(); } if (instance) { return instance; } instance = this; var _init = function (config) { if (typeof config !== 'undefined'){ if (typeof config.WPJsonApi !== 'undefined') { this.WPJsonApi = new WPJsonApi(config.WPJsonApi); //WPJsonApi configuration provided while init } else { this.WPJsonApi = new WPJsonApi(); } if (typeof config.FBJsWrapper !== 'undefined') { this.FBJsWrapper = new FBJsWrapper(config.FBJsWrapper); //FBJsWrapper configuration provided while init } else { this.FBJsWrapper = new FBJsWrapper(); } } else { this.WPJsonApi = new WPJsonApi(); this.FBJsWrapper = new FBJsWrapper(); } }; //expose the methods and variables this.init = _init; }; })(); /** The main Utility Class - End **/ // Expose the Utility to the global object window.FBAppUtil = new FBAppUtil(); })(window); </code></pre> <blockquote> <p><em><strong>FBAppUtil.FBJsWrapper.share(someArticleUrl, someCallback);</em></strong> <em><strong>FBAppUtil.FBJsWrapper.unshare(someId, someCallback);</em></strong></p> </blockquote>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload