Tag Archives: Twitter

Single User OAuth using Zend Framework’s Twitter Service Class

Michelangelo van Dam wrote a neat little article to help with single user Twitter access.  What that means is that you can set up a Twitter application and then start tweeting without having to use a username and password.  Michelangelo did it in the context of a Zend Framework MVC application and so this blog post is only about doing what he did using the Twitter service as a library instead of the whole kit ‘n kiboodle.  Basically, here’s your code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$config = array(
  'username' => 'kpschrade',
  'consumerKey' => 'CONSUMER_KEY',
  'consumerSecret' => 'CONSUMER_SECRET',
  Zend_Oauth_Token_Access::TOKEN_PARAM_KEY => 'TOKEN',
  Zend_Oauth_Token_Access::TOKEN_SECRET_PARAM_KEY => 'SECRET'
);
$accessToken = new Zend_Oauth_Token_Access();
$accessToken->setToken($config[Zend_Oauth_Token_Access::TOKEN_PARAM_KEY]);
$accessToken->setTokenSecret($config[Zend_Oauth_Token_Access::TOKEN_SECRET_PARAM_KEY]);
$twitter = new Zend_Service_Twitter();
$twitter->setLocalHttpClient($accessToken->getHttpClient($config));
 
$result = $twitter->account->verifyCredentials();
 
if (isset($result->screen_name)) {
echo 'verified';
} else {
echo 'not verified';
}

Phonegap and Bootstrap not lovers?

Just a quick post.  I’ve been doing some work with Phonegap over the past week, really liking where I was going.  One of the reasons was because I was using the Twitter Bootstrap components which was making my stuff look not horrible.  Most of the pages were doing OK.  While their were definitely slower than native (I have a fairly old Android phone) the performance was quite liveable…

… except for one page.

I was mocking it up and it was doing fine on my desktop (duh!).  But then I decided to try running it on my Android phone and weird things were happening.  The pages were loading slowly and were at about the limit of what I would be willing to release.  The one page that I was referring to has a couple of form fields, some indentions, some list elements and a couple of buttons.  It is not horribly complicated.  But, my goodness, was it slow.

It was slow to the point that I started thinking about how I was going to write this app in Java and Objective-C.  I checked out Appcelerator again.  But the things that I wanted to stay away from, namely having to learn new languages, kept on haunting me.  I remembered that someone somewhere said that the HTML UI component that Phonegap used was not optimized, but I couldn’t believe it was THAT un-optimized.  So I tried running it on my phone’s browser and it ran smoothly.  Very smoothly.

Talk about being depressed.  So I started removing anything extraneous.  I removed JQuery and installed Zepto.  Got rid of Bootstrap and started doing my CSS by hand.  Moved primary development to XCode (UGH!).

But it started taking me a LOT longer to build out the same things.  A lot of this was because Phonegap has a really nice workflow if you can do most of what you need to do on the browser.  But getting rid of all of the things I did started introducing other instabilities in the UI.

At this point I hadn’t thought of simply getting rid of Bootstrap on the original codebase and seeing what the difference was.  But this afternoon I took my broken code and simply removed the bootstrap.css file.  It immediately improved.  All of the pages were fast, even the one that was really slow.  It was drawing three times a second before.  Now it was going just fine.  Again, not quite as fast as native, but well within my tolerance.

This is all too bad, really.  I really like Bootstrap.  I am not a great designer and so being able to use those gentlemen’s work really helped me out.  But, alas, when doing work with Phonegap it seems like the overhead is just a little too much.

So what I’m doing now is merging my two codebases.  I will be putting in the JQuery stuff back in and merging it with some helper code that I wrote.

Sharing feedback with Twitter using Bit.ly – Part 2

In our previous installment we looked at setting our backend up so it could automatically retrieve the bit.ly URL for a given URL and store it as part of the data for a given instance of a Content model.  What we're going to do this time is take a look at the front end components.

Sometimes I find that doing things backwards can actually make things a little more clear.  That way you can see the end result and then, as you work backwards, see how all the pieces work together.

With that in mind, let's start with our view code, since that's the most important part of the whole thing.  The first thing we are going to do is define our HTML.

<div id="sliderMessage">Message is previewed before it is sent</div>
<div id="sliderContainer">
<div id="slider" style="width: 80%; margin: auto;"></div>
<div style="width: 191px; margin-right: 40px;">
<div id="customTwitterMessage">
<textarea id="twitterMessage" name="twitterMessage"></textarea>
<font size="1">(Maximum characters: 140)
You have characters left.</font>
 </div>
<div id="kudoTweetButton">
<a href="" target="_blank"><span style="color: white;">Tweet!</span></a></div>
<div class="kudos" id="slider-5" style="display: block;">Great Post!</div>
<div class="kudos" id="slider-4">Good Post</div>
<div class="kudos" id="slider-3">Decent Post</div>
<div class="kudos" id="slider-2">Didn't Like</div>
<div class="kudos" id="slider-1">Not Good</div>
</div>
</div>
<div id="sliderThanks">Thanks!</div>

There are a few elements in here.  The first is the slider with the ID of "slider".  The slider allows you to choose how high you want to rate the individual posting.  After that we have some code for writing custom Twitter messages if the review is really low.  It has the requisite 140 character limitation on it.  That is relatively simple to do, so I won't go into counting the characters.

Below that is the Tweet button. It floats to the right, so it is printed before our ratings.  After that are DIV tags that contain the individual messages.  They all have an ID that corresponds to the value of the slider and are all hidden, to start out with, except for "slider-5".  As the slider moves, each box will be displayed.

Rating the post

We have a couple of page-specific JavaScript variables that we need to have.  None of them are "required" to do this, but they are what makes it a little more automated.  All of the view script values are set in a controller.

var currentSlider = 5;
var twitterUser = "<php echo $this->twitterUser ?>";
var bitLy = "<php echo $this->content->getBitly() ?>";
$tags = array();
foreach ($this->content->getTags() as $t) {
    $t = (string) $t;
    $tags[] = '#' . preg_replace('/[W_]/', '', $t);
}
?>
var contentTags = ;
var twitterText = "";

currentSlider is the default value for the rating.  twitterUser is for if you rate a posting badly you can mention the Twitter user instead of just saying it sucked.  In other wordsd, it gives them a chance to redeem themselves.  bitLy is the variable that contains the bit.ly URL that we had before.  After that we echo out all of the tags that we have, but making them a little friendlier to Twitter but removing an non-white space and the underscore, since tags on Twitter generally don't have underscores.  It also adds the hash on the front of each tag.  They are then rendered as JSON because that's the easiest way to pass the information to the JavaScript in the view.  twitterText contains the full message that will be sent.

Speaking of twitterText we need to be able to set it.  That is done via the writeNormalTwitterMessage() function.  Is there an "abnormal" Twitter message?  Yep, but we'll look at that later.

function writeNormalTwitterMessage()
{
    $("#customTwitterMessage").hide();
    count = 0;
    twitterText = $("#slider-" + currentSlider).text() + " " + bitLy + " ";
    while (twitterText.length < 140 && count < contentTags.length

           && twitterText.length + contentTags[count].length < 140) {
        twitterText += " " + contentTags[count++];
    }
    twitterText = escape(twitterText);
}

Because this function is only called when the slider is moved, the custom message box is first hidden.  It is only used for non-kudos.  Then it takes the value of the currently selected DIV element and starts the string with that value, appending the bit.ly value to the end of it.  Then it iterates over a loop, adding the tags that we had created previously until we reach the 140 character limit or run out of tags.  Then we escape that value and store it on the twitterText varialbe.

Now we have to implement the functionality in the slider so that when we slide it, it is able to actually set the message in the function we had just defined.

if (twitterUser && bitLy) {
    $("#slider").slider({
        min: 1,
        max: 5,
        value:5,
        slide: function(event, ui) {
            $("#slider-" + currentSlider).hide();
            $("#slider-" + ui.value).show();
            currentSlider = ui.value;
            if (ui.value >= 3) {
                writeNormalTwitterMessage();    
            } else {
                if (ui.value == 1 ) {
                    $("#twitterMessage").val("@" + twitterUser + " " + bitLy + " wasn't good because ");
                } else {
                    $("#twitterMessage").val("@" + twitterUser + " I didn't like " + bitLy + " because ");
                }
                setTwitterMessageLength();
                $("#customTwitterMessage").show();
            }
        }
    });
    writeNormalTwitterMessage();
}

It looks like a bunch of code, but it's not.  What we do is bind to the slider and use some JSON to configure it.  We set the min as 1, the max as 5 and the default value as 5, or fully awesome kudos.  Then, for the slide event we define our functionality.  We first hide the previous slider caption DIV and then show the new one, resetting the previous value for the new one so we can hide it when we slide it again.  Then we check the value of the slider that was passed.  If it is greater or equal to 3 then the author did a good job and all we want to do is post the kudos.  If the value is 2 or 1, we want to give the author the chance to redeem him, or herself.  So we set it to give you  a text box.

The last thing to do from this side is to actually submit the text.  However, Twitter, for very good reasons, does not allow a web page to kick off some JavaScript and post a status update.  Otherwise you'd be seeing Twitter accounts being used as a spambot the likes of which you have never seen.  You could do it via an API, but your blog post isn't so important that someone will grant your website permission to do anything for them. So, to post this to Twitter, rather than using a form, we simply present a URL to be clicked on.  And the way we present that is via this code.

$("#kudoTweetButton").mouseover(function(){
    url = "http:///home?status="+twitterText;
    $("#kudoTweetButton a").attr("href", url);
});

What this does is set the href attribute to our twitterText value so that when the user clicks on it they will be brought to the Twitter page with kudos pre-populated.  It will look something like this.

Kudos post

The Twitter user then clicks "update" and the kudo is delivered.

Try it yourself a little bit and see what you think.

Sharing feedback with Twitter using Bit.ly – Part 1

Over here there is a good article on sharing page feedback on Twitter.  I'll end up doing something similar but in a different manner.  A little while I posted an article (Do you queue?) on how you could use the Zend Server Job Queue to run individual tasks.  Well you can do the same thing here.  I've made a few changes since that article, namely that Zend_Application is passed in both to execute() and to run() so I could easily retrieve application settings.  Don't know why I didn't think of that earlier.  Oh well.

The way this is different is that I want to pre-populate the bit.ly URL for an individual page so I only have to make one call to bit.ly.  This is because I don't want to slow the performance of my web page down when someone is viewing the page.  Additionally, I don't want to have a time out when I submit a page.  Therefore I need to create a task.

class Admin_Task_Bitly extends Esc_Queue_TaskAbstract
{

    private $_contentKey;
    
    public function __construct($contentKey)
    {
        $this->_contentKey = $contentKey;
    }
    
    protected function _execute (Zend_Application $app)
    {
        $ct = new Model_DbTable_Content();
        $c = $ct->find($this->_contentKey)->current();
        if (!$c) return;
        // Don't need to do it again
        if ($c->getBitly()) return;
        $options = $app->getOption('bitly');
        $url = 'http://api.bit.ly/shorten?version=2.0.1&longUrl='
               . urlencode(
                       $options['url']
                    . $c->getPageId())
               . '&login='
               . $options['login']
               . '&apiKey='
               . $options['key']
               . '&format=json';
        $results = json_decode(
            file_get_contents($url),
            true
        );
        if (!$results['errorCode']) {
            $res = array_shift($results['results']);
            $c->setBitly($res['shortUrl']);
            $c->save();
        }
    }
}

What this code does is take the primary key of the content being shortened and stores it for serialization when being passed to the Job Queue.  Then when the Job Queue executes it will get a new instance of the object, check to see if it already has a bit.ly URL and then build the URL, sending it to the bit.ly API servers.  When it gets the response back it checks to see if it's an error and if not, saves the result to the database for later usage.

But we're not quite there yet.  We still have some application.ini settings to set.

bitly.login = "xxxxxxxxxx"
bitly.url = http://www.eschrade.com/page/
bitly.key = "xxxxxxxxxxxxxx"

… not that some of those are hard to find out.  And then we need to add the task to the code that saves the article after I have finished working on it.

$mod->save();
$task = new Admin_Task_Bitly($mod->getId());
$task->execute($this->getInvokeArg('bootstrap')->getApplication());

That's it, from the functional perspective.  All that's left to do now is test it.

class Admin_Task_BitlyTest extends PHPUnit_Framework_TestCase
{
 
    public function testGetBitly()
    {
        $ct  = new Model_DbTable_Content();
        $c = $ct->fetchAll()->current();
        /* @var $c Model_Content */
        $id = $c->getId();
        $c->setBitly('');
        $c->save();
        
        $task = new Admin_Task_Bitly($c->getId());
        $task->run(Zend_Registry::get('Zend_Application'));
        
        $c = $ct->find($id)->current();
        $this->assertNotEquals(
            '',
            $c->getBitly()
        );
        
    }
}

And we're done.  Except for the actual part about posting to Twitter.  But we'll do that in a bit.

A really quick, no API, Zend_Service_Twitter example

One of the cool things in Zend Framework is the Zend_Service layer.  What it basically does is provide access to a variety of different service-based… well, services, so that you can easily integrate your application with other services.  One example is, of course, Twitter.  Often, to connect to various services you need to have an API key that you use to connect.  The same is true for Twitter, except for public feed information.  What this means is that you can search Twitter for free, with no API, easily.

I will assume that you are using the Autoloader.  Pretty much all of my scripts start with this.

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);

So, to do a quick, API-free Twitter search:

$jobs = $notAnnoying = 0;
$ts = new Zend_Service_Twitter_Search('json');
$sr = $ts->search('#php');
foreach (array_shift($sr) as $res) {

if (strpos($res['text'], ‘#job’) === false
&& strpos($res['text'], ‘#free’) === false) {
$notAnnoying++;
echo ‘:) ‘;
} else {
$jobs++;
echo ‘:( ‘;
}

echo $res['text'] . “<Br />”;
}
echo “<br />”;
echo “Not Annoying: {$notAnnoying}<br />”;
echo “Jobs Posting: {$jobs}<br />”;

The output for this is

 

:) finally, #ZCE exam scheduled for Fri, 5 Mar 2010 at 10:00am #zend #php
:) #php User Manager updated, fixing email issues; http://bit.ly/cId3Uc
:( We're hiring. Full-time PHP Developer. You got the goods? - http://twurl.nl/6of5oj - #job #hiring #php #webdev #fb
:( We're hiring. Full-time PHP Developer. You got the goods? - http://twurl.nl/6of5oj - #job #hiring #php #webdev #fb
:) Suche einen #Programmierer, der sich mit #HTML, #PHP und #Ajax auskennt (2010-03-23 Berlin) http://bit.ly/d0aZAw
:) #php Surviving Web site traffic peaks. http://tinyurl.com/yf7qhc7
:) $bool = (0 == 'aaa'); // これがtrueになるってどういう仕様だよ #php
:( Looking to hire 2 Web Developers for #Wordpress projects, #PHP http://bit.ly/asWaRx & #CSS http://bit.ly/c5WA71 #jobs call/text 858 254 9711
:) http://guwi.com.au We're now available for freelance webdesign and development work. #css #php #javascript
:( #php #freelance Rotating Banner PHP Script by shetlandpony: Recently I hired a person to upgrade my Invision forum... http://bit.ly/9Cly9z
:( #php #freelance File Sharing Script by kusoftware: I'm looking for a script where people can upload files and shar... http://bit.ly/aopLRz
:( #php #freelance Drupal site - Corrections and items needed by nearsource: We have a Drupal site from a client. ... http://bit.ly/ctkX3F
:( #php #freelance Nonpublic project #620248 by fraserbsquared: The project is Nonpublic. Description can be read onl... http://bit.ly/di26q0
:( #php #freelance Scrape website stats 2 with myboot by koolego2: *This is a private invite project only* Require... http://bit.ly/aLRrK5
:( #php #freelance Rotating Banner PHP Script by shetlandpony: Recently I hired a person to upgrade my Invision forum... http://bit.ly/9Cly9z

Not Annoying: 6
Jobs Posting: 9

Easy

Web Analytics