Categories
Facebook Tab manager Web Development WordPress

Liked/Not Liked Content Support Added to Facebook Tab Manager for WordPress

Facebook Tab Manager 2.3 lets you specify that content should be shown or not shown depending on whether the viewer has Liked the page that your tab is embedded in.

  • Add like=”1″ to the fbtab shortcode if content should only be displayed to people who have liked the page
  • Add like=”0″ to the fbtab shortcode if content should only be displayed to people who have NOT liked the page
  • You can add an explanation in either of these two cases such as message=”You must like this page before this super-special content will be displayed.”

Update April 2011: Added an fblike shortcode as an alternative to fbtab. Takes all the same parameters, including like=”1″ and like=”0″ but I recommend using fblike to wrap around a block of content such as an image that will only be shown to new people who haven’t yet liked your page. Use fbtab if the content wrapped in the shortcode is JavaScript or other fancy coding (background).

Consider this an experimental function and test it before you use it too aggressively.
Note that when you preview the content on your own website, it will be displayed as in the “not liked” mode because it’s not being viewed from within Facebook.

Update: I added a tab to www.facebook.com/carrcomm to illustrate this better. What you will see there is the equivalent of this content, as recorded in the WordPress editor for FB Tab Manager:

Liked/Not Liked content in Facebook Tab Manager

More info: fbtab shortcode, Facebook Tab Manager home page

 

Categories
Facebook Tab manager Marketing and Publicity Web Development WordPress

How to Pull Blog Content Into a Facebook Page Tab

This is a usage example for the Facebook Tab Manager for WordPress plugin, which lets you create and edit content to be displayed on Facebook from within the WordPress editor.

This is the clippings page tab for my work as a tech journalist:

Facebook screenshot - Facebook Tab Manager
Clippings Page Tab

and here is how it was composed in WordPress

Editor screenshot - Facebook Tab Manager
Page Tab Content in the WordPress Editor

This example uses the fbtab shortcode, which allows me to pull in a category from my blog. I could also just pull in the latest posts, or a single post I wanted to feature.

I set the links to open in a new window, because the full posts won’t display properly within the narrow IFrame of my page tab. I have my template set to execute all the normal wp_head actions, one of which is a Twentyten theme action to set the page background image. I then add a little bit of CSS to provide a different background for my #content div and styling for the links (based on the color of Jupiter’s ruddy clouds).

For a finishing touch, I found a free clip art image of a pair of scissors and changed the icon to be displayed next to my tab using the Facebook Developer utility on facebook.com.

After you download the Facebook Tab Manager for WordPress plugin, there’s more documentation on how to register your content on the plugin homepage and this category of my blog. I’ve also tried to make the UI as self-explanatory as I know how.

Categories
Facebook Tab manager Web Development WordPress

Updates to Facebook Tab Manager for WordPress

I continue to update Facebook Tab Manager aggressively, maybe too much so. I really should have called the first version 0.1, as I’ve now put out so many upgrades and fixes that it’s at version 2.1 — less than two weeks after the initial release.

Facebook Tab Manager lets you prepare and edit content to be displayed on Facebook as just another kind of post within the WordPress editor. When you publish your post, Facebook tab manager displays instructions for registering your content in the Facebook Developer application. This is a good way of maximizing the free resources Facebook provides for sharing your applications and marketing messages through the social media service. The content you display on Facebook can include the output of other plugins, using WordPress APIs such as shortcodes and content filters.

You can now create either a simple page tab, or both a tab and a canvas page (you view a tab embedded within a Facebook page for your business or organization, whereas a canvas is viewed, outside the context of a page).

After some futzing around, and feedback from early users of the plugin, I also figured out how to get rid of the scroll bar, and allow the page tab content to resize, for content that exceeds the default IFrame dimension of 800px tall.

2.0

Fixing template.php display for canvas pages

1.9

Correcting tab/canvas setup code.

1.8

  • Added checkbox option to set resize / auto resize for tabs taller than 800 pixels
  • You can now create both a tab and a canvas page for your application.

1.7

  • Added options page for setting defaults, such as filters to ignore and CSS to apply
  • Clarified documentation for how to fill out the Facebook Developers form
  • Flush Rewrite Rules set to run every time on initialization (not supposed to be necessary, according to the documentation, but seems to work better on some configurations)

1.6

Refinements to shortcode function

1.5

Added  Shortcode documented on the Installation section of this document. Makes it easier to integrate blog content and JavaScript widgets such as Facebook plugins.

1.4

  • Added the ability to deactivate content filters when the fbtab template is displayed. This lets you eliminate plugin content modifications that are not appropriate for your Facebook tab.
  • Updated the instructions for configuration on Facebook.

1.1, 1.2, 1.3

Fixes to default styles handling, directory locations

1.0

First public release February 2011

Categories
Facebook Tab manager WordPress

Facebook Tab Manager for WordPress Shortcode Options

Facebook Tab Manager has been upgraded to include a few new shortcode options.

  • To include a JavaScript widget, such as one of the Facebook Social Plugins, paste it into the WordPress editor in Visual mode and wrap it with [fbtab]WIDGET-CONTENT-HERE[/fbtab]. The shortcode processing function will fix the HTML entities the editor adds on angle brackets and quotation marks.
  • To include blog post or paste content, you can use [fbtab query=”QUERY-STRING”] where the query string is something like p=1 category_name=facebook-tab-manager — see the documentation for the query_posts function for possible values. You can also use a format attribute of format="headline" or format="excerpt".
  • Update: Also see the post on conditional display of content depending on whether the user has Liked / not yet Liked your page.

See the Tab Manager tab at http://www.facebook.com/carrcomm for an example using [fbtab query=”category_name=facebook-tab-manager” format=”excerpt”]

Here is what it looks like in the editor:

fbtab shortcode

WordPress makes it easy to generate the JavaScript XFBML code used here with the utilities on the Social Plugins page. For this example, I left the ID field blank, allowing the widget to default to associating content with the page it is embedded in (in this case, the underlying web page that appears in the iframe).

Obtaining Social Plugin Code

If you’re using other WordPress plugins to generate FB integration code, some of what I’m showing here may not be necessary. But it still may be handy for some folks trying to get stuff done quickly.

Categories
Facebook Tab manager Social Media Web Development WordPress

Experiments with Facebook iframe Tabs, Facebook Tab Manager WordPress Plugin

I’ve been doing a bunch of experiments with the new Facebook iframe page tabs and have cooked up a Facebook Tab Manager plugin for WordPress, which works pretty well. This plugin lets you compose and edit content for your a tab to appear on your Facebook business page (or campaign page or nonprofit page) from within WordPress. I use custom post types to treat Facebook tabs a just another kind of post, and the content can be integrated with other plugins using WordPress Shortcodes.

The content is displayed in a very simple template with none of your site’s normal navigation, etc. That makes it easier to make it fit into a Facebook layout. I provide some default styles, but you can also add custom CSS on a per post (or per tab) basis.

You can see an example iframe Facebook tab at http://www.facebook.com/carrcomm?sk=app_188292324537166

As part of some promotional work I’ve been doing with cloud database company Caspio Inc., I’ve also created custom tabs you can see on the Caspio Facebook page and ExpressDB Facebook page. These demonstrate how data entry forms and database reports from Caspio’s online database products can be embedded in a Facebook page.

Overall, I’m finding that these iframe based tabs are much easier to work with. Previously, page tags had to be coded in FBML (the Facebook Markup Language). Now Facebook is phasing out FBML in favor of XFBML, a version of the language you can embed in any web page, which works in combination with the Facebook JavaScript SDK. XFBML can also be used within an iframe page.

Using the full set of Facebook APIs, it’s possible to do much fancier things than I’ve attempted so far. But I’m happy to see that I can also do relatively simple things easily.

Categories
Web Development WordPress

RSVPMaker WordPress Plugin Passes the Version 1.0 Mark

RSVPMaker button

Over the weekend, I posted version 1.0 of my RSVPMaker WordPress plugin … swiftly followed by version 1.1 with some bug fixes. Still, it’s a milestone, even if only an arbitrary one. I called the initial release in November 0.6, recognizing that even though I’d been playing with variations on this code for years, it would need to go through a shakedown to be generally useful to a variety of people and organizations. Now, I think it’s getting there, even though plenty of room for improvement remains.

I wrote about the learning process I’ve gone through with this project a couple of weeks ago (My Open Source Adventure with RSVPMaker). The couple of glitches that popped up with the 1.0 release weren’t show stoppers, just unexpected side effects of the latest changes. For example, I added an uninstall.php file without understanding that WordPress automatically looks for a file by that name and runs it when a user wants to delete a plugin. So I had to alter its behavior to remove database entries automatically, without prompting the user to make choices. I provided a separate cleanup.php file for users who want to clean the database more selectively.

One of the tradeoffs of open source development without a budget is everyone becomes a beta tester. Still, it was nothing that couldn’t be quickly fixed, once I became aware it was a problem.

Overall, I’m happy with the way RSVPMaker handles simple events and lets you add special parameters like RSVP start and end dates as necessary.

Categories
Web Development WordPress

My Open Source Adventure with RSVPMaker

After several years of mooching off the open source software community, I finally gave something back: an event management and RSVP tracking plugin for WordPress called RSVPMaker. You can see some sample events and respond to the RSVPs at www.rsvpmaker.com, a demonstration and documentation website I set up to support this project.

I put out the first release in November, and as of this writing RSVPMaker has been downloaded more than 800 times.

As a writer and editor whose programming skills are largely self-taught, I was somewhat hesitant to put my code out there for all to critique. But RSVPMaker has been pretty well received, particularly by people who say it’s more flexible than some other event plugins to WordPress that they have tried.

The RSVPMaker plugin is new, but it’s based on PHP code I developed and refined over a period of years for event management on websites I managed for political campaigns, community organizations, and business networking groups.

Because I found I had to adapt the event and RSVP functions to work slightly differently in several different contexts, I designed RSVPMaker to support several different event management scenarios. For example, sometimes you want to request RSVPs, and other times you just want to publicize your event. Sometimes you want to collect money online, and sometimes you don’t. If you’re signing up volunteers, maybe you want people to check off specific timeslots when they can be available. I thought of putting in a deadline for accepting RSVPs, and then someone else said he also needed to be able to specify a start date before which RSVPs would not be accepted.

I tried to provide as much flexibility as possible, and then also give the more advanced users a way of writing their own plugins to change the behavior of my plugin. Certain functions, such as the one for displaying the RSVP form, can be modified to add or change the fields on the RSVP form.

A few things about the process surprised me:

  • So far, nobody has actually critiqued the programming code. That’s both good and bad: I wouldn’t mind some constructive suggestions about how to tighten it up or make it more efficient. Mostly, people download it, try it, and give me feedback on additional or different functionality they would like to see. So far, nobody has pointed out programming flaws or sent me corrections or upgrades as PHP code.
  • I thought translation to other languages was something I wouldn’t have to think about until I got to a much more advanced phase of development. But the first person who contacted me with feedback was from Denmark, and I’ve heard from people in France and other countries as well who mentioned the need to translate portions of the user interface. I’ve gotten as far as putting in the placeholders for translated or localized phrases, but still need to find partners who speak these other languages to do the actual translation. It remains to be seen whether there will be enough interest that these volunteers will come forward.
  • I ran into problems with some functions working fine on my server and apparently those of some other website operators, but not for a few people who found the links to the event pages didn’t work. Trying to debug what was different about the systems of the people who were having problems, vs. those who weren’t, turned out to be quite a challenge. In the case of the missing links, I had to implement some of my code the “wrong” way, according to the documentation, but the only way it would work for one of these people. Then, since her problem seemed to be the exception to the rule, I made it a special tweak that only gets executed if you turn it on.

Overall, it’s been a rewarding and interesting process — and I’m just getting warmed up. I started out with release 0.6, figuring the code was a little more than half-baked. I’ve worked my way up to version 0.9 and will probably declare it to have achieved version 1.0 within the month.

Categories
Web Development WordPress

Caspio Deploy2 Plugin Published

A new WordPress plugin for integration with the Caspio online database is now available at:

http://wordpress.org/extend/plugins/caspio-deploy2/

I reviewed the previous plugin the company had published and was able to recommend a better way of implementing it, using WordPress shortcodes. Here’s a summary:

Caspio is an online database featuring web-based tools for designing database tables, forms, reports, charts and graphs, and more complicated applications like store locators with Google Maps mashups. Applications can be created without programming, and embedding them in your website is as easy as copying and pasting a code snippet.

The Caspio Deploy2 plugin enables ShortCode placeholders that further streamline the deployment of Caspio applications. The shortcodes can be used for SEO deployment of content, as well as embedding the AJAX widget used to display Caspio forms. Caspio Deploy2 replaces the earlier Caspio Deployment Control plugin (which did not use shortcodes).

The [caspio] shortcode can be wrapped around the standard block of code generated by Caspio Bridge for SEO Deployment. You should toggle from the Visual to HTML mode of the WordPress editor before pasting in the block of code, but it will work even if you paste the code into the Visual editor.

Example:
[caspio]
<!– Begin Caspio Deploy Code (for inserting in body) –>
<?php require_once(‘http://b4.caspio.com/scripts/dpload.txt’);dpload(‘http://b4.caspio.com/’,’5F422000c3f2d8h4d3c9f4b9g9e0′,’l’);?>
<!– End Caspio Deploy Code –>
[/caspio]
Alternatively, you can cut out everything except the parameters for the dpload function (make sure to include the quotes around each parameter):

[caspio]
‘http://b4.caspio.com/’,’5F422000c3f2d8h4d3c9f4b9g9e0′,’l’
[/caspio]
Or the parameters from the dpload function can be embedded in the shortcode tag as follows:
[caspio url=”http://b4.caspio.com/” key=”5F422000c3f2d8h4d3c9f4b9g9e0″ style=”l”]
For Caspio application deployment in embedded mode, you can use this shortcode placeholder which will be replaced at runtime with the standard Caspio JavaScript widget. This lets you avoid having the JavaScript code scrambled by the WordPress editor in Visual mode.
[caspio embed=”https://b4.caspio.com” key=”5F422000i9f9a0h4e2i7c9g6c9c3″]
In order to deploy Caspio Bridge SEO DataPage in WordPress, you must have:

  • Caspio Bridge account with activated SEO deployment and properly configured DataPage
  • Installed and activated the Caspio Deploy2 plugin for WordPress.

For information about Caspio Bridge see the Caspio official site at http://www.caspio.com

Categories
Web Development WordPress

Tutorial on WordPress Plugin Programming – Part 3

  • Part 1
    • Introduction, API Basics, Custom Admin Menus
  • Part 2
    • Handling form posts, setting and retreiving options, database queries and updates
  • Part 3
    • Shortcodes, contact form example, adding JavaScript

Using Shortcodes

Most of the custom functionality I’ve shown so far would only be seen by the database administrator. But what if you want to change what is displayed to website visitors? There are multiple techniques you can use. I’ve talked about using filter hooks to intercept the default content to be displayed in a post or page, alter it in some way, and then send it on for output. That’s great if you want to alter the output on every post, or on a bunch of posts with the same characteristics. But what if you have something you want to insert more selectively? Maybe you have a data entry form or a whole database report you want to display, but only on a single page of your website.

One of the best techniques I’ve found is to use a shortcode — a WordPress placeholder you include in your post or page that gets replaced at runtime with the output of your custom function.

Categories
Web Development WordPress

Tutorial on WordPress Plugin Programming – Part 2

In Part 1, I mocked up a Posts submenu for adding a quote of the day to the blog. To complete this example, I’ll show how we can process the form when it is posted, perform a basic security check, and then add the post to the blog.

  • Part 1
    • Introduction, API Basics, Custom Admin Menus
  • Part 2
    • Handling form posts, setting and retreiving options, database queries and updates
  • Part 3
    • Shortcodes, contact form example, adding JavaScript

In this case, we will use the wp_insert_post function — one of several WordPress provides for specific types of database interactions. Later in this post, I’ll cover how to create custom database queries, inserts, and updates.

The function shown below is tied to the WordPress ‘admin_init’ action, which occurs after the system has loaded its database connections and other key variables and functions but before anything has been sent to the browser. Depending on whether my processing of the form post is successful, I can redirect the user to a success or error page and then exit. I use this approach to avoid the sort of problems associated with duplicate form posts and because it mirrors the way the native WordPress functions handle things like posting a blog entry.

quote_post.php :

&lt;?php

function quote_post() {

if($_POST["qnonce"] &amp;&amp; $_POST["live"])
{
$url = admin_url().'edit.php?page=demo_post_submenu';
if(wp_verify_nonce($_POST["qnonce"], "qday") )
{
$posting = stripslashes_deep($_POST);

$quote = sprintf('&lt;p style="font-size: 20px;"&gt;%s&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;%s&lt;/strong&gt;&lt;br /&gt;%s&lt;/em&gt;&lt;/p&gt;',$posting["quote"],$posting["author"],$posting["source"]);

global $current_user;
$my_post = array(
'post_title' =&gt; 'Quote of the Day for '.date('l jS \of F Y'),
'post_content' =&gt; $quote,
'post_status' =&gt; 'publish',
'post_author' =&gt; $current_user-&gt;ID
);

// Insert the post into the database
$id = wp_insert_post( $my_post );
header("Location: ".$url."&amp;success=".$id);
exit();
}
else
{
header("Location: ".$url."&amp;error=security");
exit();
}

}
}

add_action("admin_init", "quote_post");
?&gt;

Whenever you write a function that will alter the database or cause something to be publicly displayed on your website, you need to insert some security checks. In this case, the data entry form is in a password-protected part of the website, but we still want to check to ensure any data we process is actually coming from that form.

In the markup for the form, I included this function:

wp_nonce_field("qday","qnonce");

A WordPress nonce is a security code designed to be used only once (“number used once) as an identifier for a transaction. The basic function for creating a nonce to be used in any context is $nonce= wp_create_nonce ('my-nonce');. The parameter inside the command is a code that is also used on the server side to check that a submission is valid. The format is

if (! wp_verify_nonce($nonce, 'my-nonce') ) die('Security check');

The wp_nonce_field is a utility function for creating a form input field, with the nonce code you assign as the first parameter and the name of the data entry field as the second. So in my example, we check for

if(wp_verify_nonce($_POST["qnonce"], "qday") )

If the submission passes this test, we format the form submission and create a new blog post from this data.

The result looks like this:

quote
http://www.webliberationnow.com/2010/11/28/quote-of-the-day-for-sunday-28th-of-november-2010/

Setting and Retrieving Options

Note: This needs to be updated to reflect the newer Settings API, which simplifies saving settings.

If your plugin needs to set and retrieve settings, such as default values for various functions, you can do so through the get_option and update_option functions. Typically, if you have multiple options for your plugin, you will provide a whole array of them to the update_option function and retrieve them the same way — WordPress takes care of serializing and unserializing the data automatically.

Here is an example of creating an options menu and processing input.

options.php :

&lt;?php

function demo_options()
{
$options = get_option('demo');

if ( isset($_POST['notification']) ) {
check_admin_referer('demo_action','demo_nonce_field');

$options['notification']=$_POST['notification'];
$options['thank_you_message']=stripslashes($_POST['thank_you_message']);

update_option('demo', $options);
echo '&lt;div class="updated fade"&gt;&lt;p&gt;Plugin settings saved.&lt;/p&gt;&lt;/div&gt;';
}

$action_url = $_SERVER['REQUEST_URI'];
?&gt;
&lt;div class="wrap"&gt;
&lt;div id="icon-options-general" class="icon32"&gt;&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;Notification Options&lt;/h2&gt;
&lt;form name="form1" method="post" action="&lt;?=$action_url?&gt;"&gt;
&lt;p&gt;Notification Email:&lt;br /&gt;
&lt;input type="text" name="notification" id="notification" value="&lt;?=$options["notification"]?&gt;"&gt;
&lt;/p&gt;
&lt;p&gt;Thank you message:&lt;br /&gt;
&lt;textarea name="thank_you_message" id="thank_you_message" &gt;&lt;?=$options["thank_you_message"]?&gt;&lt;/textarea&gt;
&lt;/p&gt;
&lt;?php wp_nonce_field('demo_action','demo_nonce_field'); ?&gt;
&lt;input type="submit" name="button" id="button" value="Submit" /&gt;
&lt;/form&gt;
&lt;/div&gt;
&lt;?php
}

function add_demo_options_menu() {
$page_title = $menu_title = "Demo Options";
$capability = "manage_options";
$menu_slug = "demo_options";
$function = "demo_options";
add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function);
}

add_action('admin_menu', 'add_demo_options_menu');
?&gt;

Here, instead of using the generic function for adding an administration page, I’ve used one of the variations on it, add_options_page, which will make the new menu item a submenu under settings.

Database Programming In WordPress

Functions like wp_insert_post or update_option allow us to update a WordPress website without having to access the underlying database tables directly. For example, get_option('demo') accomplishes the same thing as mysql_query("SELECT option_value FROM wp_options WHERE option_name='demo' ", $dbpointer)

However, there are times when you want to do something more custom, for which you need to be able to execute your own SQL. I’ve managed several political campaign websites that allowed people to sign up as volunteers or supporters, with a reporting function that allows a campaign manager to access a report on these volunteer signups through the WordPress administration system.

Instead of initializing a database pointer or object, you can use the built-in WordPress $wpdb data access object. One thing I always forget is that the first thing you have to do is declare $wpdb as a global variable within the function that outputs your database form or report. So a data access routine winds up looking something like this.

&lt;?php

function volunteer_report() {

global $wpdb;

if($search = $_GET["search"])
$sql = $wpdb-&gt;prepare(" SELECT * FROM volunteers WHERE last_name=%s ", $search);
else
$sql = "SELECT * FROM volunteers ORDER BY last_name";

$results = $wpdb-&gt;get_results($sql, ARRAY_A);
if($results)
foreach($results as $row)
echo $row["first_name"].' '.$row["last_name"]."
";

}

?&gt;

The $wpdb->prepare function acts like PHP’s sprintf except that it also takes care of quoting and escaping string data. Like sprintf, it makes sure that any %d placeholders are filled with numeric data. Since the $search variable is coming from a user supplied $_GET, we want to make sure we’re not feeding any improper commands into the database. In a real example, we might also use a nonce appended to the URL.

The $wpdb->get_results function gets us all the matching results for our query. I always use the ARRAY_A parameter to make it return an array, which I can then have the script iterate over using foreach.

The other common patterns for data retrieval are:

$row = $wpdb-&gt;get row($sql, ARRAY_A);

and

$last_name = $wpdb-&gt;get_var("SELECT last_name FROM volunteers WHERE id=1");

To insert or update records, we pass our SQL to $wpdb->query as shown below.

&lt;?php

if($_POST["last_name"])
{
$postdata = array_map( 'stripslashes_deep', $_POST );
// code to retrieve and validate post values goes here
$sql = $wpdb-&gt;prepare("INSERT INTO volunteers SET first_name=%s, last_name=%s",$postdata["first_name"],$postdata["last_name"]);
$status = $wpdb-&gt;query($sql);
//test for errors
if($status == false)
echo "An error occurred";
else
echo "Thank you";
}

?&gt;

The use of the stripslashes_deep routine is recommended in the WordPress docs to make your plugin work consistently across systems that may or may not have the PHP “magic quotes” function turned on. Instead of letting PHP escape quotation marks automatically, you want to do it as part of your own data management routines — for example, by using $wpdb->prepare.

I’ve shown several examples that access custom database tables, but you can also access the standard WordPress tables but just interact with them a little differently. In this example, I use a filter on ‘the_content’ to append a list of post revisions to any page or post that is accessed with the query string ?show_revisions=1 — see http://www.webliberationnow.com/?show_revisions=1.

Note that instead of using the full name of the table I’m querying, I use $wpdb->posts. That’s because although the default install creates the table as wp_posts, some administrators opt to change the ‘wp_’ prefix to something else in their wp-config.php file. So $wpdb->posts will still work, even though the table name has been changed to xyz_posts in an attempt to frustrate SQL-based hack attacks.

filter.php :

&lt;?php

function content_revisions($content) {

if($_GET["show_revisions"])
{
global $wpdb;
global $post;
$sql = "SELECT post_status, post_name, post_date FROM $wpdb-&gt;posts WHERE post_parent = $post-&gt;ID";
$results = $wpdb-&gt;get_results($sql,ARRAY_A);
if($results)
{
$content .= "&lt;h3&gt;Revisions of this Post&lt;/h3&gt;";
foreach($results as $row)
{
$content .= $row["post_status"]." ".$row["post_name"]." ".$row["post_date"]."&lt;br /&gt;";
}
}
}
return $content;
}

add_filter('the_content','content_revisions');

?&gt;

Next: Shortcodes, contact form example, adding JavaScript