DABR Twitter Web Client Mini Blog Yo Mama Jokes Sports Bookmarklets About Us

November 24, 2009

Expanding DABR Photo-Upload options



 

One of the most underrated parts of Dabr IMO is the Twitpic upload page. It allows you to interact with a Twitter helper service that uses the same login information as your twitter account. And since Twitter has speced out how the uploadAndPost process works, lots of other Twitter helper services use it in their API – such as YFrog, Twitgoo, Twitvid, and Posterous (which slickly reverse matches your autopost-twitter information in order to validate your posterous account). But Posterous is pretty slick all around – probably the best tumble blog/micro blog/media blog service out there and certainly the most expandable. Perhaps in the future I might try to integrate some more later.

Dabr photo upload twitpic yfrog twitgoo twitvid posterous imgurAnyways, I figure I’d give it a go to add some more services to the twitpic page – keyed off a service dropdown. The hardest part in reality was managing the options allowed by each service (i.e. URL upload instead of file upload, additional message body). In addition, I wanted to add support for a site that I use all the time – Imgur. They didn’t implement the uploadAndPost spec so I had to make a few exceptions as well as add an extra step to post the tweet along with the uploaded image file. Took a while to work out all the kinks (one large kink remaining is that Twitvid uploads don’t work LOL) but this page pretty much saves me from ever having to go to those sites again to upload images!

function twitter_twitpic_page($query) {
  // Check for oAuth credentials
  if (user_type() == 'oauth') {
    return theme('page', 'Error', '<p>You can't do media uploads while accessing Dabr using an OAuth login.</p>');
  }

  //verify that we're entering this page from posting the form  
  if ($_POST['service']) {
  
    // format and shrink/truncate associated status message
    $status = prepare_status($_POST['message'],120);
     
    // set the  api url based on the service name (all similar except imgur)
    $postUrl = 'http://'.$_POST['service'].'.com/api/uploadAndPost';

     // prepare the fields array with the common elements
    $postData = array(
      'username' => user_current_username(),
      'password' => $GLOBALS['user']['password'],
      'message' => $status,);
       
     // perform service-specific tasks
     switch ($_POST['service']){
         case 'imgur':
               if (!defined('IMGUR_API_KEY')){
                    twitter_refresh('twitpic/fail/'.$_POST['service'].'/'.urlencode('API KEY undefined'));
               }
               
               // imgur doesn't use the Twitter standard uploadAndPost spec
               $postUrl = 'http://imgur.com/api/upload.xml';
               
               //if URL is provided, send URL for imgur to rehost, otherwise use selected file
              if (empty($_POST['url'])){
                   $postData = array(
                         'key' => IMGUR_API_KEY, 
                         'image' => '@'.$_FILES['media']['tmp_name']);
              } else {
                   $postData = array(
                         'key' => IMGUR_API_KEY, 
                         'image' => $_POST['url']);
              }
               break;
          case 'yfrog': 
               if (!defined('YFROG_API_KEY')){
                    twitter_refresh('twitpic/fail/'.$_POST['service'].'/'.urlencode('API KEY undefined'));
               }
               $postData['key'] = YFROG_API_KEY;
               
              // Yfrog can also accept a URL as the media source
              if (!empty($_POST['url'])){
                   $postData['url'] = $_POST['url'];
              } else {
                    $postData['media'] = '@'.$_FILES['media']['tmp_name'];
               }
               break;
          case 'posterous':
              // Posterous has an additional comments element and will fall through to the default
               $postData['body'] = stripslashes($_POST['body']);
         default:
               $postData['media'] = '@'.$_FILES['media']['tmp_name'];
     }

     // make the api call
    $response = twitter_process($postUrl,$postData);

     // check response XML for associated IDs
     // for uploadAndPost supporting services
    if (preg_match('#mediaid>(.*)</mediaid#', $response, $matches)) {
      $id = $matches[1];
       
       // Twitvid allows crossposting to Youtube
      if (preg_match('#youtube_id>(.*)</youtube_id#', $response, $matches)) {
           $altid = $matches[1];
      }
       
       // Posterous sends back a separate shortened URL code
      if (preg_match('#mediaurl>http://post.ly/(.*)</mediaurl#', $response, $matches)) {
           $altid = $matches[1];
      }
       
       // show success messsage and pass IDs to create links to media
      twitter_refresh("twitpic/confirm/".$_POST['service']."/$id/$altid");
    } 
     // response for imgur uses different element names
     elseif (preg_match('#image_hash>(.*)</image_hash#', $response, $matches)) {
      $id = $matches[1];
       
       // the secret delete link/code is offered only once and should be shown in the confirmation
      if (preg_match('#delete_hash>(.*)</delete_hash#', $response, $matches)) {
           $deleteid = $matches[1];
      }
       
       // we need the actual imgur link in order to post status to Twitter 
      if (preg_match('#original_image>(.*)</original_image#', $response, $matches)) {
           $imglinkid = $matches[1];
      }

       // update status with the image link
      $status .= " $imglinkid ";

       // post the status to twitter
      $response = twitter_process('http://twitter.com/statuses/update.json',
           array('source' => 'dabr', 'status' => $status));

       // show success message, link to image, and hidden delete link
      twitter_refresh("twitpic/confirm/".$_POST['service']."/$id/$deleteid");
    } else {
          // collect errors from response
         if (preg_match('#error_code>(.*)</error_code#', $response, $matches)) {
              $error = "Error Code: $matches[1]. ";
         }
         if (preg_match('#error_msg>(.*)</error_msg#', $response, $matches)) {
              $error .= "Error Msg: $matches[1]. ";
         }
         if (preg_match('#err.code="(.*)" msg="(.*)" />#', $response, $matches)) {
               $error = "Error Code: $matches[1] Error Msg: $matches[2]";
         }
            
          // show failure messages
         twitter_refresh('twitpic/fail/'.$_POST['service'].'/'.urlencode($error));
    }
  } 
  // Page mode to show confirmation and media links
  elseif ($query[1] == 'confirm') {
    $postedlink = "Media: http://$query[2].com/$query[3]";
    if ($query[2] == 'imgur' && !empty($query[4])){
         $postedlink .= " // Hidden delete link: http://imgur.com/delete/$query[4]";
    }
    if ($query[2] == 'twitvid' && !empty($query[4])){
         $postedlink .= " // Youtube:  http://www.youtube.com/watch?v=$query[4]";
    }
    if ($query[2] == 'posterous' && !empty($query[4])){
         $postedlink = "Post: http://post.ly/$query[4]";
    }
     
     // show formatted confirmation to add thumbnails
    $content = "<p>Upload success.</p><p>".twitter_parse_tags($postedlink)."</p>";
  } 
  // page mode for failure - show errors
  elseif ($query[1] == 'fail') {
    $content = "<p>$query[2] upload failed: $query[3]</p>";
  } 
  // otherwise show form
  else {
    $content = '<style type="text/css">.disabletrue{background:#ddd;}.disablefalse{background:#fff;}</style>
          <script type="text/javascript">function disable(sel){service=sel.options[sel.selectedIndex].value;sel.form.url.disabled= (service != 'yfrog' && service != 'imgur');sel.form.body.disabled= (service != 'posterous');sel.form.url.className= 'disable'+(service != 'yfrog' && service != 'imgur');sel.form.body.className= 'disable'+(service != 'posterous');}</script>
          <form name="twitpic" method="post" action="twitpic" enctype="multipart/form-data"><br />Service: <select name="service" onChange="disable(this);"><option value="twitpic">Twitpic</option><option value="twitgoo">Twitgoo</option><option value="yfrog">Yfrog</option><option value="imgur">Imgur</option><option value="twitvid">Twitvid</option><option value="posterous">Posterous</option></select><br />Upload: <input type="file" name="media" /><br />Message: <input type="text" name="message" maxlength="120" /><br />Image Url: <input type="text" name="url" maxlength="120" /><br />Body:<br><textarea style="position:relative;top:-14px;left:67px;" cols=50 rows=3" name="body"></textarea><br /><input type="submit" value="Upload" /></form>
          <script type="text/javascript">disable(document.forms['twitpic'].service);</script>';
  }
  return theme('page', 'Twitpic Upload', $content);
}

I also had to update the twitter_photo_replace function, which I use to show the thumbnail of the image on the confirm page, to add the imgur regexes

    '#imgur.com/([w]{5})[s.ls][.w]*#i' => 'http://imgur.com/%ss.png',
    '#imgur.com/gallery/([w]+)#i' => 'http://imgur.com/%ss.png',
    '#imgur.com/delete/([w]+)#i' => 'images/trash.gif',

A lot of code, but to me it’s worth it!

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: unranked [?]

More DABR Goodness: Unshortening Short URLs



 

I’m loving DABR – makes me want to find a way to load XAMPP on my phone. Anyways I’ve made a few more tweaks to my codebase recently. Inspired by this Terrence Eden post…:Expanding URLs in Dabr / Twitter – Terence Eden’s Blog

I hate shortened URLs with a passion. It makes it hard to see what a link is and whether I’ve visited it before. If they fail – like tr.im threatened to do – you lose your links with no way to see where they once went.

So, hurrah for LongURLPlease – a service which takes those horrid little links and turns them in to full sized URLs.

…I went about implementing this longurl scheme and having the long URL show in the tweet. But to make it so that it the tweet isn’t dominated by a 600 character URL (and reduce some of the benefit of short URLs) I figured I could try to make it only display the domain name of each link while still keeping the actual link in the HTML – much like they do at slashdot. Easy peasy, I thought. Just uncomment the LONGURL_KEY in config.php and move the gwt code from the twitter_parse_links_callback function to the theme_external_link function in common/twitter.php. Right?

function twitter_parse_links_callback($matches) {
  $url = $matches[1];
  if (substr($url, 0, strlen(BASE_URL)) == BASE_URL) return "<a href='$url'>$url</a>";
  return theme('external_link', $url);
}

function theme_external_link($url, $content = null) {
     //Long URL functionality.  Also uncomment function long_url($shortURL)
     if (!$content)
     {
          $longurl = long_url($url);
          $domain = parse_url($longurl,PHP_URL_HOST);
          if (setting_fetch('gwt') == 'on') {
               $encoded = urlencode($longurl);
               $longurl = "http://google.com/gwt/n?u=$encoded";
          }
          return "<a href='$longurl' target='_blank'>[".$domain."]</a>";
     }
     else
     {
          return "<a href='$url' target='_blank'>$content</a>";
     }
}

Well, not so much. Because of some of the mechanisms in identifying links and adding the appropriate HTML, I broke the image-service thumbnail code and had to think through how this was going to work out. It ended up with me changing the twitter_photo_replace and my twitlonger_replace functions to only return the addendums (if any) and not the entire tweet text

function twitter_photo_replace($text) {

.
.
.

  if (!empty($images)) 
     return implode(' ', $images).'<br />';
}  

function twitlonger_replace($text) {
  $tmp = strip_tags($text);

  if (preg_match('#http://tl.gd/([dw]+)#', $tmp, $matches)) {
.
.
.
     $returntext = "<p style="padding:5px;margin:5px;border:thin dashed;">$longtweet</p>";//$text";
    return theme('external_link', $matches[0], $returntext);
  } else {
     return "";
  }
}

then I changed how the twitter_parse_tags function called them and appended the data. Also important is to make it use the original unaltered tweet ($input) as a source rather than the modified one with the links ($out).

function twitter_parse_tags($input) {
.
.
.

  if (!in_array(setting_fetch('browser'), array('text', 'worksafe'))) {
    $twitlonger = twitlonger_replace($input);
    $out = twitter_photo_replace($twitlonger.$input).$twitlonger.$out;
  }
  return $out;
}

Dabr Decoding Short URLs

The results, though, are great . Nice and clean. As mentioned on other pages, the overhead may be a bit much for more than your personal web server, but I love the change. One byproduct as well is that now any posts with images in Twitlonger that don’t get into the original tweet will have their photo thumbnail displayed too. 5% scenario – yes, but still icing :)

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: unranked [?]

November 15, 2009

Integrating Ping.fm and Wordpress (quick and dirty)



 

More social networking script goodness – this time with Ping.fm. Ping.fm is a very slick service that allows you to ping, or update all your blogs, social networks or other presence apps at once via the web, IM, SMS, email, and a variety of other ways. For example, you can update your Facebook and Twitter with the same status from IM, post new photo blogs to your Flickr and your Tumble blog from your Cell phone, or update your Blogger, LiveJournal and Wordpress.com blogs via email. It’s extremely flexible and customizable with groups, directives. link tracking and more. It could very well be the glue that keeps all our online identities in sync.

The one hole it does have (that affects me) is the self-hosted Wordpress blog. There are lots of cleaner alternatives to this problem and surely this little code exercise of mine isn’t meant to be a replacement for a good wordpress plugin like Matt Jacob’s Ping.fm WordPress Plugin. It’s just a quick and dirty way to get things working in a hurry.

I cobbled it together based on some code to post to the WP database that I found from DanBrown on SitePoint Forums as well as the Ping.fm Developer docs. It’s a page that also allows for some testing with form inputs. Place it in the root of your Wordpress installation and set up the page link in Ping.fm as a Custom URL. Hope it helps someone!


Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: unranked [?]

Roll Your Own Twitter Web Client



 

As I get more and more into this Twitter thing, I’ve struggled like everyone else to find the perfect web client. I prefer web clients over heavy desktop apps because they can be used on any platform from mobile phone to Windows, Mac or Linux browser. The Twitter web client (both the regular and the mobile versions) just don’t work. Too much of one thing and not enough of another.

In my searching, I came across one mobile effort that seemed to combine the best features of the best desktop apps with the ease and flexibility of a web client. And better yet for a tinkerer like me – it was open source:dabr.co.uk – About

Dabr is a mobile web interface to Twitter’s API. It has been described as m.twitter.com on steroids, and tries to provide as much mobile functionality as possible.

It’s an open source project created and maintained by @davidcarrington, inspired by some suggestions from @whatleydude – who remains a keen user and strategic advisor on many of the features that make dabr what it is today.

The project is built on your feedback, literally! Your ideas aren’t just welcome, but are the driving force that makes Dabr improve and hopefully be your mobile twitter client of choice. So please get in touch :)

Great. This is a quality effort that includes tweeting, deleting, retweeting, replying, favoriting, following and unfollowing, blocking, searching, trends, hashtags, and even Twitpic! So immediately when loading this code on my machine (code and instructions here) I set about implementing a different url shortening service – u.nu. Not only are url’s shorter than the default bit.ly service, it also doesn’t require me to set up an account. If it’s free it’s me! The other feature I wanted to add in was calling TweetShrink for tweets over 140 characters. I add some code also to truncate with ellipses if Tweetshrink doesn’t achieve the desired savings, but this could easily be changed to do multiple tweets. Maybe that’s the next tweak!

UPDATE (Nov 19): Added decoding for imgur images and TwitLonger tweets.

Here are the code changes (in bold) that I made to twitter.php. just do a find for the function names as the file is quite large:

function twitter_url_shorten_callback($match) {
  if (preg_match('#http://www.flickr.com/photos/[^/]+/(\d+)/#', $match[0], $matches)) {
    return 'http://flic.kr/p/'.flickr_encode($matches[1]);
  }
  return file_get_contents("http://u.nu/unu-api-simple?url=" . $match[0]);
}

function twitter_update() {
  twitter_ensure_post_action();
  $status = twitter_url_shorten(stripslashes(trim($_POST['status'])));
  if ($status) {
    if (strlen($status)>140)
    	$status1 = file_get_contents("http://tweetshrink.com/shrink?format=string&text=" . urlencode($status));
    if ($status1 && strlen($status1)<141)
    	$status=$status1;
    if (strlen($status)>140)
    	$status=substr($status,0,137) . "...";

    $request = 'http://twitter.com/statuses/update.json';
    $post_data = array('source' => 'dabr', 'status' => $status);
    $in_reply_to_id = (string) $_POST['in_reply_to_id'];
    if (is_numeric($in_reply_to_id)) {
      $post_data['in_reply_to_status_id'] = $in_reply_to_id;
    }
    $b = twitter_process($request, $post_data);
  }
  twitter_refresh($_POST['from'] ? $_POST['from'] : '');
}

function twitter_parse_tags($input) {
  $out = preg_replace_callback('#(\w+?://[\w\#$%&~/.\-;:=,?@\[\]+]*)(?<![.,])#is', 'twitter_parse_links_callback', $input);
  $out = preg_replace('#(^|\s)@([a-z_A-Z0-9]+)#', '$1@<a href="user/$2">$2</a>', $out);
  $out = preg_replace('#(^|\s)(\\#([a-z_A-Z0-9:_-]+))#', '$1<a href="hash/$3">$2</a>', $out);
  if (!in_array(setting_fetch('browser'), array('text', 'worksafe'))) {
    $out = twitter_photo_replace($out);
    $out = twitlonger_replace($out);
  }
  return $out;
}

function twitlonger_replace($text) {
  $tmp = strip_tags($text);

  if (preg_match_all('#tl.gd/([\d\w]+)#', $tmp, $matches, PREG_PATTERN_ORDER) > 0) {
    foreach ($matches[1] as $key => $match) {
      $longtweetraw = file_get_contents('http://tl.gd/'.$match);
      $longtweet = preg_replace(array('/\n/','/<br..>/')," ",$longtweetraw);
      $longtweet = preg_replace('/(.*)id="replybutton"(.*)\<p\>(.*)\<\/p\>(.*)p class="clientlink"(.*)/',"$3",$longtweet);
      $longtweets[] = theme('external_link', "http://{$matches[0][$key]}", "<p style=\"padding:5px;margin:5px;border:thin dashed;\">$longtweet</p>");
    }
  }

  if (empty($longtweets)) return $text;
  return implode('', $longtweets).''.$text;
}

function twitter_photo_replace($text) {
  $images = array();
  $tmp = strip_tags($text);

  // List of supported services. Array format: pattern => thumbnail url
  $services = array(
    '#youtube\.com\/watch\?v=([_-\d\w]+)#i' => 'http://i.ytimg.com/vi/%s/1.jpg',
    '#twitpic.com/([\d\w]+)#i' => 'http://twitpic.com/show/thumb/%s',
    '#imgur.com/([\d\w]{5})[ls]?[\.\w]+#i' => 'http://imgur.com/%ss.png',
    '#imgur.com/gallery/([\d\w]{5})#i' => 'http://imgur.com/%ss.png',

.
.
.

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: unranked [?]

Improved Sharing and Twitter Bookmarklet



 

Since my last forays with Mini-blogging (or microblogging) and bookmarkletting, lots of things have changed. Specifically for this purpose, the url-shortening service urlTea was yanked. It forced me to switch to tinyurl and then to tr.im – which also almost went down. Along with increasingly sharing more links on sites other than Twitter, I combined some of my other bookmarklets into an all-in-one server-driven bookmarklet bonanza.

This new server side script will facilitate sharing links via Twitter, Facebook, email, BB Code forums, blogs/Myspace. Since the Twitter explosion this year – many more cool url shortening services have opened and offered API’s like is.gd and u.nu. Twitter now uses bit.ly/j.mp but since it needed a login, I didn’t bother. There is no more JSON, just straight API calls.

This new code still solves the problem of allowing you to provide context for your tweet or other form of sharing – and also integrates flexibility amongst using shortened URLs or not, and which shortening service to use. It can be used as a stand-alone script, or as a bookmarklet. The bookmarklet still captures the link, title, and any selected text on the page you want to share. It can be set to automatically forward to Twitter, or to stay on the helper popup. In the case of the latter, there is still a button that allows you to forward the link, title, and any selected text to Twitter where you handle editing and the character count.

The beauty of this approach is – if you don’t want to share on Twitter, or also want to share on other sites, you can get the code needed to post on MySpace or Facebook or by email or on sites that use BBCode. Here is the bookmarklet. As usual – drag it to your Bookmarks or Links bar, or simply bookmark it.

Twiit or Auto-Twiit

As always, the bookmarklet is a simple link that contains javascript. You click it whenever you’re on a web page that you want to share. Again, I offer no appologies or warranties. Take it and make it yours if you like it. Or just use the bookmarklet here and let me know how it works.

The PHP:

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: unranked [?]

November 6, 2007

Microblogging With urlTea – My Twitter Bookmarklet



 

UPDATE: Improved Sharing and Twitter Bookmarklet | myopiclunacy.com


As you know I’m trying the whole Mini-Blog thing now as a way of microblogging my thoughts – mostly on links to pages/stories I come across. Using a Twitter account and a plug-in called Twitter Tools from Alex King, I can show my Mini Blog on the left side, and then all the mini-blog entries/tweets for the day (using GMT time, unfortunately) get posted into a daily blog entry the next day. It’s a real cool tool.

Using Twitter for microblogging has become popular especially because Twitter will take any link it finds in your tweet and HTMLize it. One problem that I came across, though, was the cumbersome way of getting links and context into the tweet – a lot of copying and pasting going on. Because of the 140 character limit, Twitter has some kind of feature where it uses TinyUrl to shorten the urls posted into the status, but I couldn’t find a bookmarklet that would include the page title and a selection of text. Reason being, probably, this isn’t a highly requested feature. So I tried writing my own.

I did it in a somewhat cumbersome way, by using a bookmarklet to get the page URL, title, and selected text and call a PHP form that uses JSON to connect to the urlTea url shortening service. It then autoposts the form to the Twitter status window ready to be updated. All of this in a pop-up window and, of course, assuming you’re already signed into Twitter. I let Twitter handle the character count and any additional editing – especially when the page title and selected text become too long. The page title, the urlTea shortened link, and any selected text are copied in that order separated by a space.

TwitIt or TwrimIt

The bookmarklet is a simple link that contains javascript. You drag it to your link toolbar (or just bookmark it from the right-click menu) and click it whenever you want to microblog. (If you’re new to bookmarklets – they’re an awesome timesaver. Check out the other bookmarklets I use.) I’m sure this smacks of hackery and is rife with style infractions. I offer no appologies or warranties. Take it and make it yours if you like it. Or just use the bookmarklet here and let me know how it works.

Update (11/14/07): So after looking over a few pages, and the inexplicable problems I had with the JSON api, I’ve updated the script to use the regular API. Which I can switch to use TinyUrl if I need to. I suppose I could then have it use the twitter API but I want the intermediate editing step.

Update (1/17/09): Fixed issues with slashes and escaped code. Also added an option for using tr.im URL shortening service – which is shorter than tinyurl. Works much better. If you want to use tr.im, choose the TwrimIt bookmarklet. All this is pending an API key from the good folks at tr.im. BTW – urlTea is now defunct in case you were wondering!

The PHP page is real simple and portable:

<?php

$url = $_GET["url"];
$r1 = array(’”‘);
$r2 = array(”“”);
$title = str_replace($r1,$r2,stripslashes(htmlspecialchars(urldecode($_GET["title"]))));
$text = str_replace($r1,$r2,stripslashes(htmlspecialchars(urldecode($_GET["text"]))));
$urlt = file_get_contents(”http://tr.im/api/trim_simple?url=” . $url);
$urlt = file_get_contents(”http://tinyurl.com/api-create.php?url=” . $url);
// $urlt = file_get_contents(”http://tr.im/api/trim_simple?url=” . $url);
$status = “$title $urlt $text”
?>

<html>
<head>
<title>myopiclunacy.com’s Twitter TinuURl/tr.im submitter</title>
</head>
<body>
<form name=twit id=twit method=”get” action=”http://twitter.com/home”>
<input type=”hidden” name=”status” value=”<?php echo $status; ?>”>
</form>
<script type=”text/javascript”>
document.forms[0].submit();
</script>
</body>

If you’re looking for something for the Blackberry – check out this article from Tao of Mac.

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: 7% [?]

November 5, 2007

Mini Blog for 2007-11-05



 

Powered by Twitter Tools.

Bookmark and Share Email This Print This PDF This Blog This Facebook Twiit Google Google Yahoo! Buzz Ping StumbleUpon LinkedIn MySpace

Popularity: 1% [?]

Powered by WordPress

Blog Information