initial commit based on SVN rev. 711

This commit is contained in:
Matthias Kretschmann 2012-02-26 03:28:54 +01:00
commit 1a30d966d7
362 changed files with 10338 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
# osx noise
.DS_Store
profile
# xcode noise
build/*
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata
# svn & cvs
.svn
CVS

51
admin/admin-ajax.php Normal file
View File

@ -0,0 +1,51 @@
<?php
define('YOURLS_AJAX', true);
define('YOURLS_ADMIN', true);
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
yourls_maybe_require_auth();
// This file will output a JSON string
header('Content-type: application/json');
if( !isset( $_REQUEST['action'] ) )
die();
// Pick action
$action = $_REQUEST['action'];
switch( $action ) {
case 'add':
yourls_verify_nonce( 'add_url', $_REQUEST['nonce'], false, 'omg error' );
$return = yourls_add_new_link( $_REQUEST['url'], $_REQUEST['keyword'] );
echo json_encode($return);
break;
case 'edit_display':
yourls_verify_nonce( 'edit-link_'.$_REQUEST['id'], $_REQUEST['nonce'], false, 'omg error' );
$row = yourls_table_edit_row ( $_REQUEST['keyword'] );
echo json_encode( array('html' => $row) );
break;
case 'edit_save':
yourls_verify_nonce( 'edit-save_'.$_REQUEST['id'], $_REQUEST['nonce'], false, 'omg error' );
$return = yourls_edit_link( $_REQUEST['url'], $_REQUEST['keyword'], $_REQUEST['newkeyword'], $_REQUEST['title'] );
echo json_encode($return);
break;
case 'delete':
yourls_verify_nonce( 'delete-link_'.$_REQUEST['id'], $_REQUEST['nonce'], false, 'omg error' );
$query = yourls_delete_link_by_keyword( $_REQUEST['keyword'] );
echo json_encode(array('success'=>$query));
break;
case 'logout':
// unused for the moment
yourls_logout();
break;
default:
yourls_do_action( 'yourls_ajax_'.$action );
}
die();

280
admin/index.php Normal file
View File

@ -0,0 +1,280 @@
<?php
define( 'YOURLS_ADMIN', true );
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
yourls_maybe_require_auth();
// Variables
$table_url = YOURLS_DB_TABLE_URL;
// Default SQL behavior
$where = $search_display = $search_text = $search_url = $url = $keyword = '';
$search_in_text = 'URL';
$search_in_sql = 'url';
$sort_by_text = 'Short URL';
$sort_by_sql = 'timestamp';
$sort_order_text = 'Descending Order';
$sort_order_sql = 'desc';
$page = ( isset( $_GET['page'] ) ? intval($_GET['page']) : 1 );
$search = ( isset( $_GET['s_search'] ) ? htmlspecialchars( trim($_GET['s_search']) ) : '' );
$perpage = ( isset( $_GET['perpage'] ) && intval( $_GET['perpage'] ) ? intval($_GET['perpage']) : 15 );
$link_limit = ( isset( $_GET['link_limit'] ) && !empty( $_GET['link_limit'] ) ) ? intval($_GET['link_limit']) : '' ;
if ( $link_limit !== '' ) {
$link_filter = ( isset( $_GET['link_filter'] ) && $_GET['link_filter'] == 'more' ? 'more' : 'less' ) ;
$link_moreless = ( $link_filter == 'more' ? '>' : '<' );
$where = " AND clicks $link_moreless $link_limit";
} else {
$link_filter = '';
}
$date_filter = 'before';
$date_first = $date_second = '';
$base_page = yourls_admin_url( 'index.php' );
// Searching
if( !empty($search) && !empty($_GET['s_in']) ) {
switch($_GET['s_in']) {
case 'keyword':
$search_in_text = 'Short URL';
$search_in_sql = 'keyword';
break;
case 'url':
$search_in_text = 'URL';
$search_in_sql = 'url';
break;
case 'title':
$search_in_text = 'Title';
$search_in_sql = 'title';
break;
case 'ip':
$search_in_text = 'IP Address';
$search_in_sql = 'ip';
break;
}
$search_text = stripslashes($search);
$search_display = "Searching for <strong>$search_text</strong> in <strong>$search_in_text</strong>. ";
$search_url = "&amp;s_search=$search_text &amp;s_in=$search_in_sql";
$search = str_replace('*', '%', '*'.$search.'*');
$where .= " AND `$search_in_sql` LIKE ('$search')";
}
// Time span
if( !empty($_GET['date_filter']) ) {
switch($_GET['date_filter']) {
case 'before':
$date_filter = 'before';
if( yourls_sanitize_date( $_GET['date_first'] ) ) {
$date_first_sql = yourls_sanitize_date_for_sql( $_GET['date_first'] );
$where .= " AND `timestamp` < '$date_first_sql'";
$date_first = $_GET['date_first'];
}
break;
case 'after':
$date_filter = 'after';
if( yourls_sanitize_date( $_GET['date_first'] ) ) {
$date_first_sql = yourls_sanitize_date_for_sql( $_GET['date_first'] );
$where .= " AND `timestamp` > '$date_first_sql'";
$date_first = $_GET['date_first'];
}
break;
case 'between':
$date_filter = 'between';
if( yourls_sanitize_date( $_GET['date_first'] ) && yourls_sanitize_date( $_GET['date_second'] ) ) {
$date_first_sql = yourls_sanitize_date_for_sql( $_GET['date_first'] );
$date_second_sql = yourls_sanitize_date_for_sql( $_GET['date_second'] );
$where .= " AND `timestamp` BETWEEN '$date_first_sql' AND '$date_second_sql'";
$date_first = $_GET['date_first'];
$date_second = $_GET['date_second'];
}
break;
}
}
// Sorting
if( !empty($_GET['s_by']) || !empty($_GET['s_order']) ) {
switch($_GET['s_by']) {
case 'keyword':
$sort_by_text = 'Short URL';
$sort_by_sql = 'keyword';
break;
case 'url':
$sort_by_text = 'URL';
$sort_by_sql = 'url';
break;
case 'timestamp':
$sort_by_text = 'Date';
$sort_by_sql = 'timestamp';
break;
case 'ip':
$sort_by_text = 'IP Address';
$sort_by_sql = 'ip';
break;
case 'clicks':
$sort_by_text = 'Clicks';
$sort_by_sql = 'clicks';
break;
}
switch($_GET['s_order']) {
case 'asc':
$sort_order_text = 'Ascending Order';
$sort_order_sql = 'asc';
break;
case 'desc':
$sort_order_text = 'Descending Order';
$sort_order_sql = 'desc';
break;
}
}
// Get URLs Count for current filter, total links in DB & total clicks
list( $total_urls, $total_clicks ) = array_values( yourls_get_db_stats() );
if ( $where ) {
list( $total_items, $total_items_clicks ) = array_values( yourls_get_db_stats( $where ) );
} else {
$total_items = $total_urls;
$total_items_clicks = false;
}
// This is a bookmarklet
if ( isset( $_GET['u'] ) ) {
$is_bookmark = true;
yourls_do_action( 'bookmarklet' );
$url = yourls_sanitize_url( $_GET['u'] );
$keyword = ( isset( $_GET['k'] ) ? yourls_sanitize_keyword( $_GET['k'] ) : '' );
$title = ( isset( $_GET['t'] ) ? yourls_sanitize_title( $_GET['t'] ) : '' );
$return = yourls_add_new_link( $url, $keyword, $title );
// If fails because keyword already exist, retry with no keyword
if ( isset( $return['status'] ) && $return['status'] == 'fail' && isset( $return['code'] ) && $return['code'] == 'error:keyword' ) {
$msg = $return['message'];
$return = yourls_add_new_link( $url, '', $ydb );
$return['message'] .= ' ('.$msg.')';
}
// Stop here if bookmarklet with a JSON callback function
if( isset( $_GET['jsonp'] ) && $_GET['jsonp'] == 'yourls' ) {
$short = $return['shorturl'] ? $return['shorturl'] : '';
$message = $return['message'];
header('Content-type: application/json');
echo yourls_apply_filter( 'bookmarklet_jsonp', "yourls_callback({'short_url':'$short','message':'$message'});" );
die();
}
$s_url = stripslashes( $url );
$where = " AND `url` LIKE '$s_url' ";
$page = $total_pages = $perpage = 1;
$offset = 0;
$text = ( isset( $_GET['s'] ) ? stripslashes( $_GET['s'] ) : '' );
// This is not a bookmarklet
} else {
$is_bookmark = false;
// Checking $page, $offset, $perpage
if(empty($page) || $page == 0) { $page = 1; }
if(empty($offset)) { $offset = 0; }
if(empty($perpage) || $perpage == 0) { $perpage = 50; }
// Determine $offset
$offset = ($page-1) * $perpage;
// Determine Max Number Of Items To Display On Page
if(($offset + $perpage) > $total_items) {
$max_on_page = $total_items;
} else {
$max_on_page = ($offset + $perpage);
}
// Determine Number Of Items To Display On Page
if (($offset + 1) > ($total_items)) {
$display_on_page = $total_items;
} else {
$display_on_page = ($offset + 1);
}
// Determing Total Amount Of Pages
$total_pages = ceil($total_items / $perpage);
}
// Begin output of the page
$context = ( $is_bookmark ? 'bookmark' : 'index' );
yourls_html_head( $context );
yourls_html_logo();
yourls_html_menu() ;
if ( !$is_bookmark ) { ?>
<p><?php echo $search_display; ?></p>
<p>Display <strong><?php echo $display_on_page; ?></strong> to <strong class='increment'><?php echo $max_on_page; ?></strong> of <strong class='increment'><?php echo $total_items; ?></strong> URLs<?php if( $total_items_clicks !== false ) echo ", counting <strong>$total_items_clicks</strong> " . yourls_plural('click', $total_items_clicks) ?>.</p>
<?php } ?>
<p>Overall, tracking <strong class='increment'><?php echo number_format($total_urls); ?></strong> links, <strong><?php echo number_format($total_clicks); ?></strong> clicks, and counting!</p>
<?php yourls_html_addnew(); ?>
<?php
// If bookmarklet, add message. Otherwise, hide hidden share box.
if ( !$is_bookmark ) {
yourls_share_box( '', '', '', '', '<h2>Your short link</h2>', '<h2>Quick Share</h2>', true );
} else {
echo '<script type="text/javascript">$(document).ready(function(){
feedback( "' . $return['message'] . '", "'. $return['status'] .'");
init_clipboard();
});</script>';
}
yourls_table_head();
if ( !$is_bookmark ) {
$params = array(
'search_text' => $search_text,
'search_in_sql' => $search_in_sql,
'sort_by_sql' => $sort_by_sql,
'sort_order_sql' => $sort_order_sql,
'page' => $page,
'perpage' => $perpage,
'link_filter' => $link_filter,
'link_limit' => $link_limit,
'total_pages' => $total_pages,
'search_url' => $search_url,
'date_filter' => $date_filter,
'date_first' => $date_first,
'date_second' => $date_second,
);
yourls_html_tfooter( $params );
}
yourls_table_tbody_start();
// Main Query
$where = yourls_apply_filter( 'admin_list_where', $where );
$url_results = $ydb->get_results("SELECT * FROM `$table_url` WHERE 1=1 $where ORDER BY `$sort_by_sql` $sort_order_sql LIMIT $offset, $perpage;");
$found_rows = false;
if( $url_results ) {
$found_rows = true;
foreach( $url_results as $url_result ) {
$keyword = yourls_sanitize_string( $url_result->keyword );
$timestamp = strtotime( $url_result->timestamp );
$url = stripslashes( $url_result->url );
$ip = $url_result->ip;
$title = $url_result->title ? $url_result->title : '';
$clicks = $url_result->clicks;
echo yourls_table_add_row( $keyword, $url, $title, $ip, $clicks, $timestamp );
}
}
$display = $found_rows ? 'display:none' : '';
echo '<tr id="nourl_found" style="'.$display.'"><td colspan="6">No URL</td></tr>';
yourls_table_tbody_end();
yourls_table_end();
if ( $is_bookmark )
yourls_share_box( $url, $return['shorturl'], $title, $text );
?>
<?php yourls_html_footer( ); ?>

79
admin/install.php Normal file
View File

@ -0,0 +1,79 @@
<?php
define( 'YOURLS_INSTALLING', true );
define( 'YOURLS_ADMIN', true );
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
require_once( YOURLS_INC.'/functions-install.php' );
$error = array();
$warning = array();
$success = array();
// Check pre-requisites
if ( !yourls_check_database_version() )
$error[] = 'MySQL version is too old. Ask your server admin for an upgrade.';
if ( !yourls_check_php_version() )
$error[] = 'PHP version is too old. Ask your server admin for an upgrade.';
// Check additional stuff
if ( !yourls_check_curl() )
$warning[] = 'PHP extension <tt>cURL</tt> is not installed. This server won\'t be able to use the remote API';
// Is YOURLS already installed ?
if ( yourls_is_installed() ) {
$error[] = 'YOURLS already installed.';
// check if .htaccess exists, recreate otherwise. No error checking.
if( !file_exists( YOURLS_ABSPATH.'/.htaccess' ) ) {
yourls_create_htaccess();
}
}
// Start install if possible and needed
if ( isset($_REQUEST['install']) && count( $error ) == 0 ) {
// Create/update .htaccess file
if ( yourls_create_htaccess() ) {
$success[] = 'File <tt>.htaccess</tt> successfully created/updated.';
} else {
$warning[] = 'Could not write file <tt>.htaccess</tt> in YOURLS root directory. You will have to do it manually. See <a href="http://yourls.org/htaccess">how</a>.';
}
// Create SQL tables
$install = yourls_create_sql_tables();
if ( isset( $install['error'] ) )
$error = array_merge( $error, $install['error'] );
if ( isset( $install['success'] ) )
$success = array_merge( $success, $install['success'] );
}
// Start output
yourls_html_head( 'install', 'Install YOURLS' );
?>
<div id="login">
<form method="post" action="?"><?php // reset any QUERY parameters ?>
<p>
<img src="<?php echo YOURLS_SITE; ?>/images/yourls-logo.png" alt="YOURLS" title="YOURLS" />
</p>
<?php
// Print errors, warnings and success messages
foreach ( array ('error', 'warning', 'success') as $info ) {
if ( count( $$info ) > 0 ) {
echo "<ul class='$info'>";
foreach( $$info as $msg ) {
echo '<li>'.$msg."</li>\n";
}
echo '</ul>';
}
}
// Display install button or link to admin area if applicable
if( !yourls_is_installed() && !isset($_REQUEST['install']) ) {
echo '<p>&nbsp;</p><p style="text-align: center;"><input type="submit" name="install" value="Install YOURLS" class="button" /></p>';
} else {
if( count($error) == 0 )
echo '<p>&nbsp;</p><p style="text-align: center;">&raquo; <a href="'.yourls_admin_url().'" title="YOURLS Administration Page">YOURLS Administration Page</a></p>';
}
?>
</form>
</div>
<?php yourls_html_footer(); ?>

158
admin/plugins.php Normal file
View File

@ -0,0 +1,158 @@
<?php
define( 'YOURLS_ADMIN', true );
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
yourls_maybe_require_auth();
// Handle plugin administration pages
if( isset( $_GET['page'] ) && !empty( $_GET['page'] ) ) {
yourls_plugin_admin_page( $_GET['page'] );
}
// Handle activation/deactivation of plugins
if( isset( $_GET['action'] ) ) {
// Check nonce
yourls_verify_nonce( 'manage_plugins', $_REQUEST['nonce'] );
// Check plugin file is valid
if( isset( $_GET['plugin'] ) && yourls_validate_plugin_file( YOURLS_PLUGINDIR.'/'.$_GET['plugin'].'/plugin.php') ) {
global $ydb;
// Activate / Deactive
switch( $_GET['action'] ) {
case 'activate':
$result = yourls_activate_plugin( $_GET['plugin'].'/plugin.php' );
if( $result === true )
yourls_redirect( yourls_admin_url( 'plugins.php?success=activated' ), 302 );
break;
case 'deactivate':
$result = yourls_deactivate_plugin( $_GET['plugin'].'/plugin.php' );
if( $result === true )
yourls_redirect( yourls_admin_url( 'plugins.php?success=deactivated' ), 302 );
break;
default:
$result = 'Unsupported action';
break;
}
} else {
$result = 'No plugin specified, or not a valid plugin';
}
yourls_add_notice( $result );
}
// Handle message upon succesfull (de)activation
if( isset( $_GET['success'] ) ) {
if( $_GET['success'] == 'activated' OR $_GET['success'] == 'deactivated' ) {
yourls_add_notice( 'Plugin '.$_GET['success'] );
}
}
yourls_html_head( 'plugins', 'Manage Plugins' );
yourls_html_logo();
yourls_html_menu();
?>
<h2>Plugins</h2>
<?php
$plugins = (array)yourls_get_plugins();
$count = count( $plugins );
$count_active = yourls_has_active_plugins();
?>
<p id="plugin_summary">You currently have <strong><?php echo $count.' '.yourls_plural( 'plugin', $count ); ?></strong> installed, and <strong><?php echo $count_active; ?></strong> activated</p>
<table id="main_table" class="tblSorter" cellpadding="0" cellspacing="1">
<thead>
<tr>
<th>Plugin Name</th>
<th>Version</th>
<th>Description</th>
<th>Author</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php
$nonce = yourls_create_nonce( 'manage_plugins' );
foreach( $plugins as $file=>$plugin ) {
// default fields to read from the plugin header
$fields = array(
'name' => 'Plugin Name',
'uri' => 'Plugin URI',
'desc' => 'Description',
'version' => 'Version',
'author' => 'Author',
'author_uri' => 'Author URI'
);
// Loop through all default fields, get value if any and reset it
foreach( $fields as $field=>$value ) {
if( isset( $plugin[ $value ] ) ) {
$data[ $field ] = $plugin[ $value ];
} else {
$data[ $field ] = '(no info)';
}
unset( $plugin[$value] );
}
$plugindir = trim( dirname( $file ), '/' );
if( yourls_is_active_plugin( $file ) ) {
$class = 'active';
$action_url = yourls_nonce_url( 'manage_plugins', yourls_add_query_arg( array('action' => 'deactivate', 'plugin' => $plugindir ) ) );
$action_anchor = 'Deactivate';
} else {
$class = 'inactive';
$action_url = yourls_nonce_url( 'manage_plugins', yourls_add_query_arg( array('action' => 'activate', 'plugin' => $plugindir ) ) );
$action_anchor = 'Activate';
}
// Other "Fields: Value" in the header? Get them too
if( $plugin ) {
foreach( $plugin as $extra_field=>$extra_value ) {
$data['desc'] .= "<br/>\n<em>$extra_field</em>: $extra_value";
unset( $plugin[$extra_value] );
}
}
$data['desc'] .= "<br/><small>plugin file location: $file</small>";
printf( "<tr class='plugin %s'><td class='plugin_name'><a href='%s'>%s</a></td><td class='plugin_version'>%s</td><td class='plugin_desc'>%s</td><td class='plugin_author'><a href='%s'>%s</a></td><td class='plugin_actions actions'><a href='%s'>%s</a></td></tr>",
$class, $data['uri'], $data['name'], $data['version'], $data['desc'], $data['author_uri'], $data['author'], $action_url, $action_anchor
);
}
?>
</tbody>
</table>
<script type="text/javascript">
yourls_defaultsort = 0;
yourls_defaultorder = 0;
<?php if ($count_active) { ?>
$('#plugin_summary').append('<span id="toggle_plugins">filter</span>');
$('#toggle_plugins').css({'background':'transparent url("../images/filter.gif") top left no-repeat','display':'inline-block','text-indent':'-9999px','width':'16px','height':'16px','margin-left':'3px','cursor':'pointer'})
.attr('title', 'Toggle active/inactive plugins')
.click(function(){
$('#main_table tr.inactive').toggle();
});
<?php } ?>
</script>
<p>If something goes wrong after you activate a plugin and you cannot use YOURLS or access this page, simply rename or delete its directory, or rename the plugin file to something different than <code>plugin.php</code>.</p>
<h3>More plugins</h3>
<p>For more plugins, head to the official <a href="http://code.google.com/p/yourls/wiki/PluginList">Plugin list</a>.</p>
<?php yourls_html_footer(); ?>

105
admin/tools.php Normal file
View File

@ -0,0 +1,105 @@
<?php
define( 'YOURLS_ADMIN', true );
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
yourls_maybe_require_auth();
yourls_html_head( 'tools', 'Cool YOURLS Tools' );
yourls_html_logo();
yourls_html_menu();
?>
<div class="sub_wrap">
<h2>Bookmarklets</h2>
<p>YOURLS comes with <span>four</span> handy <span>bookmarklets</span> for easier link shortening.</p>
<h3>Standard or Instant, Simple or Custom</h3>
<ul>
<li>The <span>Standard Bookmarklets</span> will take you to a page where you can easily edit or delete your brand new short URL.</li>
<li>The <span>Instant Bookmarklets</span> will pop the short URL without leaving the page you are viewing.</li>
<li>The <span>Simple Bookmarklets</span> will generate a short URL with a random or sequential keyword</li>
<li>The <span>Custom Keyword Bookmarklets</span> will prompt you for a custom keyword first</li>
</ul>
<p>With the Standard Bookmarklets you will also get a <span>Quick Share</span> tool box to make posting to Twitter, Facebook or Friendfeed a snap. If you want to share a description along with the link you're shortening, simply <span>select text</span> on the page you're viewing before clicking on your bookmarklet link</p>
<h3>The Bookmarklets</h3>
<p>Click and drag links to your toolbar (or right-click and bookmark it)</p>
<table class="tblSorter" cellpadding="0" cellspacing="1">
<thead>
<tr>
<td>&nbsp;</td>
<th>Standard (new page)</th>
<th>Instant (popup)</th>
</tr>
</thead>
<tbody>
<tr>
<th class="header">Simple</th>
<td><a href="javascript:(function()%7Bvar%20d=document,w=window,enc=encodeURIComponent,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),s2=((s.toString()=='')?s:('%22'+enc(s)+'%22')),f='<?php echo yourls_admin_url('index.php'); ?>',l=d.location,p='?u='+enc(l.href)+'&t='+enc(d.title)+'&s='+s2,u=f+p;try%7Bthrow('ozhismygod');%7Dcatch(z)%7Ba=function()%7Bif(!w.open(u))l.href=u;%7D;if(/Firefox/.test(navigator.userAgent))setTimeout(a,0);else%20a();%7Dvoid(0);%7D)()" class="bookmarklet" onclick="alert('Drag to your toolbar!');return false;">Shorten</a></td>
<td><a href="javascript:(function()%7Bvar%20d=document,s=d.createElement('script');window.yourls_callback=function(r)%7Bif(r.short_url)%7Bprompt(r.message,r.short_url);%7Delse%7Balert('An%20error%20occured:%20'+r.message);%7D%7D;s.src='<?php echo yourls_admin_url('index.php'); ?>?u='+encodeURIComponent(d.location.href)+'&jsonp=yourls';void(d.body.appendChild(s));%7D)();" class="bookmarklet" onclick="alert('Drag to your toolbar!');return false;">Instant Shorten</a></td>
</tr>
<tr>
<th class="header">Custom Keyword</th>
<td><a href="javascript:(function()%7Bvar%20d=document,w=window,enc=encodeURIComponent,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),s2=((s.toString()=='')?s:('%22'+enc(s)+'%22')),f='<?php echo yourls_admin_url('index.php'); ?>',l=d.location,k=prompt(%22Custom%20URL%22),k2=(k?'&k='+k:%22%22),p='?u='+enc(l.href)+'&t='+enc(d.title)+''+s2+k2,u=f+p;if(k!=null)%7Btry%7Bthrow('ozhismygod');%7Dcatch(z)%7Ba=function()%7Bif(!w.open(u))l.href=u;%7D;if(/Firefox/.test(navigator.userAgent))setTimeout(a,0);else%20a();%7Dvoid(0)%7D%7D)()" class="bookmarklet" onclick="alert('Drag to your toolbar!');return false;">Custom shorten</a></td>
<td><a href="javascript:(function()%7Bvar%20d=document,k=prompt('Custom%20URL'),s=d.createElement('script');if(k!=null){window.yourls_callback=function(r)%7Bif(r.short_url)%7Bprompt(r.message,r.short_url);%7Delse%7Balert('An%20error%20occured:%20'+r.message);%7D%7D;s.src='<?php echo yourls_admin_url('index.php'); ?>?u='+encodeURIComponent(d.location.href)+'&k='+k+'&jsonp=yourls';void(d.body.appendChild(s));%7D%7D)();" class="bookmarklet" onclick="alert('Drag to your toolbar!');return false;">Instant Custom Shorten</a></td>
</tr>
</tbody>
</table>
<h2>Prefix-n-Shorten</h2>
<p>When viewing a page, you can also prefix its full URL: just head to your browser's address bar, add "<span><?php echo preg_replace('@https?://@', '', YOURLS_SITE); ?>/</span>" to the beginning of the current URL (right before its 'http://' part) and hit enter.</p>
<p>Note: this will probably not work if your web server is running on Windows <?php if( yourls_is_windows() ) echo '(which seems to be the case here)'; ?>.</p>
<?php if( yourls_is_private() ) { ?>
<h2>Secure passwordless API call</h2>
<p>YOURLS allows API calls the old fashioned way, using <tt>username</tt> and <tt>password</tt>
parameters. If you're worried about sending your credentials into the wild, you can also make API
calls without using your login or your password, using a secret signature token.</p>
<p>Your secret signature token: <strong><code><?php echo yourls_auth_signature(); ?></code></strong>
(It's a secret. Keep it secret)</p>
<p>This signature token can only be used with the API, not with the admin interface.</p>
<ul>
<li><h3>Usage of the signature token</h3>
<p>Simply use parameter <tt>signature</tt> in your API requests. Example:</p>
<p><code><?php echo YOURLS_SITE; ?>/yourls-api.php?signature=<?php echo yourls_auth_signature(); ?>&action=...</code></p>
</li>
<li><h3>Usage of a time limited signature token</h3>
<pre><code>&lt;?php
$timestamp = time();
<tt>// actual value: $time = <?php $time = time(); echo $time; ?></tt>
$signature = md5( $timestamp . '<?php echo yourls_auth_signature(); ?>' );
<tt>// actual value: $signature = "<?php $sign = md5( $time. yourls_auth_signature() ); echo $sign; ?>"</tt>
?>
</code></pre>
<p>Now use parameters <tt>signature</tt> and <tt>timestamp</tt> in your API requests. Example:</p>
<p><code><?php echo YOURLS_SITE; ?>/yourls-api.php?timestamp=<strong>$timestamp</strong>&signature=<strong>$signature</strong>&action=...</code></p>
<p>Actual values:<br/>
<tt><?php echo YOURLS_SITE; ?>/yourls-api.php?timestamp=<?php echo $time; ?>&signature=<?php echo $sign; ?>&action=...</tt></p>
<p>This URL would be valid for only <?php echo YOURLS_NONCE_LIFE; ?> seconds</p>
</li>
</ul>
<p>(See the <a href="<?php echo YOURLS_SITE; ?>/readme.html#API">API documentation</a> for more)</p>
</div>
<?php } // end is private ?>
<?php yourls_html_footer(); ?>

88
admin/upgrade.php Normal file
View File

@ -0,0 +1,88 @@
<?php
define( 'YOURLS_ADMIN', true );
define( 'YOURLS_NO_UPGRADE_CHECK', true ); // Bypass version checking to prevent loop
require_once( dirname(dirname(__FILE__)).'/includes/load-yourls.php' );
require_once( YOURLS_INC.'/functions-upgrade.php' );
require_once( YOURLS_INC.'/functions-install.php' );
yourls_maybe_require_auth();
yourls_html_head( 'upgrade', 'Upgrade YOURLS' );
yourls_html_logo();
yourls_html_menu();
?>
<h2>Upgrade YOURLS</h2>
<?php
// Check if upgrade is needed
if ( !yourls_upgrade_is_needed() ) {
echo '<p>Upgrade not required. Go <a href="'.yourls_admin_url('index.php').'">back to play</a>!</p>';
} else {
/*
step 1: create new tables and populate them, update old tables structure,
step 2: convert each row of outdated tables if needed
step 3: - if applicable finish updating outdated tables (indexes etc)
- update version & db_version in options, this is all done!
*/
// From what are we upgrading?
if ( isset( $_GET['oldver'] ) && isset( $_GET['oldsql'] ) ) {
$oldver = yourls_sanitize_version( $_GET['oldver'] );
$oldsql = yourls_sanitize_version( $_GET['oldsql'] );
} else {
list( $oldver, $oldsql ) = yourls_get_current_version_from_sql();
}
// To what are we upgrading ?
$newver = YOURLS_VERSION;
$newsql = YOURLS_DB_VERSION;
// Verbose & ugly details
$ydb->show_errors = true;
// Let's go
$step = ( isset( $_GET['step'] ) ? intval( $_GET['step'] ) : 0 );
switch( $step ) {
default:
case 0:
echo "
<p>Your current installation needs to be upgraded.</p>
<p>Please, pretty please, it is recommended that
you <strong>backup</strong> your database<br/>(you should do this regularly anyway)</p>
<p>Nothing awful <em>should</em> happen, but this doesn't mean it <em>won't</em> happen, right? ;)</p>
<p>On every step, if <span class='error'>something goes wrong</span>, you'll see a message and hopefully a way to fix</p>
<p>If everything goes too fast and you cannot read, <span class='success'>good for you</span>, let it go :)</p>
<p>Once you are ready, press Upgrade!</p>
<form action='upgrade.php?' method='get'>
<input type='hidden' name='step' value='1' />
<input type='hidden' name='oldver' value='$oldver' />
<input type='hidden' name='newver' value='$newver' />
<input type='hidden' name='oldsql' value='$oldsql' />
<input type='hidden' name='newsql' value='$newsql' />
<input type='submit' class='primary' value='Upgrade' />
</form>";
break;
case 1:
case 2:
$upgrade = yourls_upgrade( $step, $oldver, $newver, $oldsql, $newsql );
break;
case 3:
$upgrade = yourls_upgrade( 3, $oldver, $newver, $oldsql, $newsql );
$admin = yourls_admin_url('index.php');
echo "
<p>Your installation is now up to date !</p>
<p>Go back to <a href='$admin'>the admin interface</a></p>
";
}
}
?>
<?php yourls_html_footer(); ?>

78
changelog.txt Normal file
View File

@ -0,0 +1,78 @@
*** YOURLS Changelog ***
This file attempts to list the main changes through all versions of YOURLS. For a much more detailed
list, simply refer to the commit messages: http://code.google.com/p/yourls/source/list
1.0
- initial release
1.0.1
- don't remember. Trivial stuff probably.
1.1
- don't remember. Some little bugs I guess.
1.2
- don't remember. A few tiny stuff for sure.
1.3-RC1
- added bookmarklet and tools page
- improved XSS filter when adding new URL
- code cleanup in admin/index.php to separate code and display
- added favicon
- stricter coding to prevent notices with undefined indexes
- hide PHP notices & SQL errors & warnings, unless YOURLS_DEBUG constant set to true
1.4
- added an upgrader from 1.3 to 1.4
- change in logic: now using a global object $ydb for everything related to DB and other globally needed stuff
- change in logic: include "load-yourls.php" instead of "config.php" to start engine
- change in DB schema: now storing URLs with their keyword as used in shorturl, allowing for any keyword length
- change in DB schema: new table for storing various options including next_id, dropping table of the same name
- change in DB schema: new table for storing hits (for stats)
- improved the installer, with .htaccess file creation
- layout tweak: now prettier, isn't it?
- stats! OMG stats!
1.4.1
- fixed base 62 URLs (keywords with MiXeD CaSe)
- new & secure auth method for API calls, with no need to use login & password combo
- allow SSL enforcement for admin pages
- new API method: stats for individual URL.
- prevent internal redirection loops
- filter and search URLs & short URLs by date
1.4.2
- fixed bug in auth function
- added sample public API file
- added check in API requests for WordPress plugin when adding a new short URL
- prettier sample public interface
1.4.3
- fixed bug no-stats-showing-ffs due to inconsistency in DB schema
- improve error reporting with API method url-stat
1.5
- added: plugin architecture! OMG plugins!!1!!1!
- added: directory /user, config.php can be moved there
- added: new "instant bookmarklets"
- added: 1 click copy-to-clipboard a la bitly
- change in logic: now all request are handled by PHP and don't rely on .htaccess
- added: saving URL titles
- added: support for prefix-n-shorten: sho.rt/http://example.com/
- added: core plugin to allow hyphens in URLs
- added: core sample plugin to wrap redirected URLs in a social toolbar
- added: core sample plugin to show how to create administration page in plugins
- added: core plugin to display a random pretty background
- changed: layout now using a more consistent palette, see http://yourls.org/palette
- added: anti XSS and anti CSRF measures
- added: interactive map if possible in stat traffic by countries
- fixed: lots of bugs
1.5.1
- added: full jsonp support
- added: ability to use encrypted passwords in the config file
- fixed: support for http://www.sho.rt/bleh and http://sho.rt/bleh
- fixed: bugs, bugs, bugs
- added: hooks, hooks, hooks
- improved: things, things, things

14
css/cal.css Normal file
View File

@ -0,0 +1,14 @@
/* Calendar */
.datepicker { border-collapse: collapse; border: 2px solid #999; position: absolute; }
.datepicker tr.controls th { height: 22px; font-size: 11px; }
.datepicker select { font-size: 11px; }
.datepicker tr.days th { height: 18px; }
.datepicker tfoot td { height: 18px; text-align: center; text-transform: capitalize; }
.datepicker th, .datepicker tfoot td { background: #eee; font: 10px/18px Verdana, Arial, Helvetica, sans-serif; }
.datepicker th span, .datepicker tfoot td span { font-weight: bold; }
.datepicker tbody td { width: 24px; height: 24px; border: 1px solid #ccc; font: 11px/22px Arial, Helvetica, sans-serif; text-align: center; background: #fff; }
.datepicker tbody td.date { cursor: pointer; }
.datepicker tbody td.date.over { background-color: #99ffff; }
.datepicker tbody td.date.chosen { font-weight: bold; background-color: #ccffcc; }
/* Form defaults */
#date_and, #date_second {display:none}

113
css/infos.css Normal file
View File

@ -0,0 +1,113 @@
h3 span.label {
width:100px;
display:inline-block;
}
ul.toggle_display {
display:none;
list-style-type:none;
margin-left:0;
margin-right:23px;
padding:12px 5px 3px;
border-bottom:1px solid #C7E7FF;
}
ul.toggle_display li {
padding:0;
}
#tabs ul#headers li, #tabs ul#headers li h2, #stats_lines li{
display: inline;
margin-right: 10px;
}
#tabs ul#headers {
border-bottom:1px solid #E3F3FF;
padding:12px 5px 3px 5px;
float:left;
}
.wrap_unfloat {
overflow:hidden;
}
#tabs ul#headers li a {
color:#595441;
border:1px solid #C7E7FF;
-moz-border-radius:10px 10px 0 0;
-webkit-border-radius:10px 10px 0 0;
border-radius:10px 10px 0 0;
padding:10px 5px 5px 15px;
background:#E3F3FF;
}
#tabs ul#headers li a:hover {
text-decoration:none;
background:#88C0EB;
}
#tabs ul#headers li a.selected {
border-bottom:2px solid #fff;
background:#fff;
}
#tabs ul#headers li a.selected:hover {
background:#fff;
}
#stats_lines li a {
-moz-border-radius:10px 10px 0 0;
-webkit-border-radius:10px 10px 0 0;
border-radius:10px 10px 0 0;
padding:3px 10px;
background:#E3F3FF;
border:1px solid #C7E7FF;
}
#stats_lines li a:hover {
text-decoration:none;
background:#C7E7FF;
}
#stats_lines li a.selected {
background:#fff;
border:1px solid #C7E7FF;
border-bottom:1px solid white;
}
#stats_lines li a.selected:hover {
background:#fff;
}
.tab {
padding:10px;
}
li#sites_various { padding-left:22px; padding-top:4px;}
li.sites_list img, #longurl img {width:16px; height: 16px; display:inline-block;}
#referrer_cell { min-width: 300px;}
#details_clicks li.bestday {
font-weight:bold;
}
ul.no_bullet {
list-style-type: none;
margin-left:0;
padding:0;
}
ul.no_bullet li {
margin-bottom:5px;
}
#historical_clicks {
float:left;
margin:0;
}
#historical_clicks li {
padding:2px 10px;
margin:0;
}
#historical_clicks li:hover {
background:#C7E7FF !important;
}
#historical_clicks span.historical_link {
min-width:130px;
display:inline-block;
}
#historical_clicks span.historical_count {
min-width:100px;
display:inline-block;
}

BIN
css/palette.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

65
css/share.css Normal file
View File

@ -0,0 +1,65 @@
#shareboxes, #tweet {
overflow:hidden;
}
#shareboxes{
margin-top:15px;
}
div.share {
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
border:2px solid #88c0eb;
background:#fff;
margin-right:1em;
padding:0 1em;
float:left;
height:140px;
}
#origlink{
display:inline-block;
white-space:pre;
width:183px;
overflow:hidden;
vertical-align:-2px;
}
#copybox {
width:250px;
}
#sharebox {
width:500px;
}
#tweet_body {
float:left;
width:450px;
height:4em;
font-size:12px;
}
#charcount {
padding-left:5px;
color:#88c0eb;
}
#charcount.negative {
color:red;
}
#share_links a {
padding:0 12px 0 18px;
font-weight:bold
}
#share_links a:hover {
background-position:2px center;
}
#share_tw {background:transparent url(../images/twitter.png) left center no-repeat;}
#share_fb {background:transparent url(../images/facebook.png) left center no-repeat;}
#share_ff {background:transparent url(../images/friendfeed.png) left center no-repeat;}
#copylink{
cursor:pointer;
background:transparent url(../images/copy.png) 130% center no-repeat;
}
#copylink:hover, #copylink.hover {
background-position:100% 50%;
}

330
css/style.css Normal file
View File

@ -0,0 +1,330 @@
body {
font-family: Verdana, Arial;
font-size: 12px;
color: #595441;
background:#e3f3ff;
text-align:center;
margin-top:0px;
padding-top:10px;
}
#wrap {
width:950px;
min-height:150px;
margin:0 auto;
background:white;
text-align:left;
padding:5px 20px 10px 20px;
border-left:3px solid #2a85b3;
border-right:3px solid #2a85b3;
border-bottom:3px solid #2a85b3;
border-top:3px solid #2a85b3;
-moz-border-radius:20px;
-webkit-border-radius:20px;
border-radius:20px;
}
.hide-if-no-js {display: none;}
div, p, td {
font-family: Verdana, Arial;
font-size: 12px;
}
a, a:link, a:active, a:visited {
color: #2a85b3;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {height:50px;margin:0;float:right;width:500px;}
h1 a {text-align:right;font-size:20px;float:right;}
h1 a, h1 a:link, h1 a:active, h1 a:visited {color:#2a85b3}
h1 a:hover{text-decoration:none;}
h1 a:hover span{text-decoration:underline;color:#88c0eb}
ul#admin_menu {
min-height:100px;
list-style-type:none;
padding:0;
font-size:105%;
}
ul#admin_menu li {
color:#aaa;
padding:1px 0;
}
ul#admin_menu li:hover {
list-style-type:square;
color:#000;
}
code {
background:#eaeaef;
padding:0 2px;
}
tt {
background:#ffc;
padding:0 2px;
}
input, textarea {
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
}
Input.text, select, textarea {
font-family: Verdana, Arial;
font-size: 10px;
color: #595441;
background-color: #FFFFFF;
border: 1px solid #88c0eb;
margin:1px;
}
input.button {
font-family: Verdana, Arial;
font-size: 10px;
color: #595441;
font-weight: bold;
background-color: #FFFFFF;
border: 1px solid #88c0eb;
cursor:pointer;
}
input.primary {
border:2px solid #2A85B3;
background:#fafafe;
}
input.text:focus, textarea:focus {
border:2px solid #2A85B3;
margin:0px;
}
tr.edit-row td {
background:#e3f3ff !important;
}
#new_url {
text-align:center;
padding:1px;
border:1px solid #CDCDCD;
background:#fff;
clear:both;
}
#new_url div {
background:#C7E7FF;
padding:4px;
}
#new_url_form {
padding:4px;
}
#new_url #feedback {
background:#ff8;
color:#88c0eb;
width:50%;
margin:0px 25%;
padding:2px;
border:1px solid #ff8;
}
#new_url #feedback .fail {
color:#f55;
}
td.url small a{
color:#bbc;
}
body.desktop td.actions input,body.desktop td.actions a {
visibility:hidden;
}
td.actions input.disabled, td.actions input.loading {
visibility:visible;
}
tr:hover td.actions input, tr:hover td.actions a {
visibility:visible;
}
td.actions .button {
font-family: Verdana, Arial;
font-size: 10px;
color: #595441;
font-weight: bold;
background-color: #FFFFFF;
border: 1px solid #88c0eb;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
cursor:pointer;
height:22px;
width:22px;
margin-top:0px;
margin-right:5px;
display:block;
float:left;
text-indent:-9999px;
outline:0px;
}
td.actions .button:active {
border:1px solid #000;
}
td.actions .button:hover {
text-decoration:none;
}
td.actions .button.disabled, #add-button.disabled {
border:1px solid #333;
background:#ccc;
}
td.actions .button.loading, #add-button.loading {
background:#cc7 url(../images/loading.gif) center center no-repeat;
color:#cc7;
}
td.actions .button_share {
background:transparent url(../images/share.png) 2px center no-repeat;
}
td.actions .button_edit {
background:transparent url(../images/pencil.png) 2px center no-repeat;
}
td.actions .button_delete {
background:transparent url(../images/delete.png) 2px center no-repeat;
}
td.actions .button_stats {
background:transparent url(../images/chart_bar.png) 2px center no-repeat;
}
#main_table tfoot th, #main_table tfoot th div {
font-size:10px;
}
.error {
color: red;
background:#fee;
}
.warning {
color: orange;
background:#ffe9bf;
}
.success {
color: green;
background:#efe;
}
#login {
width: 300px;
margin: 200px auto 0px auto;
}
#login p{
font-weight: bold;
}
#login .text {
width: 100%;
}
#login ul {
padding-left:0px;
list-style-type:none;
text-indent:0;
}
#login ul li {
padding:0 0 5px 20px;
}
#login ul.error li {
background:transparent url(../images/cancel.png) top left no-repeat;
}
#login ul.warning li {
background:transparent url(../images/error.png) top left no-repeat;
}
#login ul.success li {
background:transparent url(../images/accept.png) top left no-repeat;
}
.sub_wrap {
width:580px;
padding-bottom:30px;
text-align:justify;
}
.sub_wrap span {
background:#ffa;
padding:0 2px;
}
a.bookmarklet {
border:2px solid #2a85b3;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
padding:5px 5px 5px 20px;
background:#eef url(../images/favicon.gif) 2px center no-repeat;
margin:3px;
display:block;
float:left;
}
a.bookmarklet:hover {
text-decoration:none;
background-position:3px center;
}
#footer {
text-align:center;
margin-top:20px;
}
#footer p {
padding:10px;
background:white;
margin:0 auto;
width:950px;
-moz-border-radius:10px;
-webkit-border-radius:10px;
border-radius:10px;
border:2px solid #2a85b3;
-moz-border-radius-bottomleft:30px;
-moz-border-radius-bottomright:30px;
-webkit-border-bottom-left-radius:25px;
-webkit-border-bottom-right-radius:25px;
border-bottom-left-radius:25px;
border-bottom-right-radius:25px;
}
#footer p a {
background:#fff url(../images/favicon.gif) 2px center no-repeat;
padding-left:20px;
}
.notice {
border:1px solid #2a85b3;
background: #F3FAFD;
-moz-border-radius:6px;
-webkit-border-radius:6px;
border-radius:6px;
width:70%;
margin-left:15%;
padding-left:10px;
margin-bottom:5px;
}
.jquery-notify-bar {
width:100%;
position:fixed;
top:0;
left:0;
z-index:32768;
background-color:#efefef;
font-size:18px;
color:#000;
text-align:center;
font-family: Arial, Verdana, sans-serif;
padding:20px 0px;
border-bottom:1px solid #bbb;
filter:alpha(opacity=95);
-moz-opacity:0.95;
-khtml-opacity:0.95;
opacity:0.95;
-moz-box-shadow: 0 1px 5px rgba(0,0,0,0.5);
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5);
text-shadow: 0 1px 1px rgba(0,0,0,0.1);
}
.jquery-notify-bar.error ,.jquery-notify-bar.fail {
color:#f00;
background-color:#fdd;
}
.jquery-notify-bar.error span,.jquery-notify-bar.fail span{
background:transparent url("../images/error.png") no-repeat left center;
padding-left:20px;
}.jquery-notify-bar.success span{
background:transparent url("../images/accept.png") no-repeat left center;
padding-left:20px;
}
.jquery-notify-bar.success {
color:#060;
background-color:#aea;
}
.notify-bar-close {
position:absolute;
left:95%;
font-size:11px;
}
tr.plugin.active a{ font-weight:bolder;}
body.desktop tr.plugin td.plugin_desc small{ visibility:hidden;}
tr:hover.plugin td.plugin_desc small{ visibility:visible;}

92
css/tablesorter.css Normal file
View File

@ -0,0 +1,92 @@
/* jQuery Table Sorter */
table.tblSorter {
font-family:Verdana, Arial;
background-color: #CDCDCD;
margin:10px 0px 0px;
font-size: 8pt;
width: 100%;
text-align: left;
}
table.tblSorter thead tr th, table.tblSorter tfoot tr th, table.tblSorter th.header {
background-color: #C7E7FF;
border: 1px solid #FFF;
font-size: 8pt;
padding: 4px;
}
table.tblSorter tfoot tr th {
background-color: #E3F3FF;
}
table.tblSorter thead tr .header {
background-image: url('../images/bg.gif');
background-repeat: no-repeat;
background-position: center right;
cursor: pointer;
padding-right:10px;
}
table.tblSorter tbody tr:hover td {
background-color:#F3FAFD;
}
table.tblSorter tbody td {
color: #3D3D3D;
padding: 4px;
background-color: #FFF;
vertical-align: top;
}
table.tblSorter tbody tr.even td {
}
table.tblSorter tbody tr.odd td {
}
table.tblSorter thead tr .headerSortUp {
background-image: url('../images/desc.gif');
}
table.tblSorter thead tr .headerSortDown {
background-image: url('../images/asc.gif');
}
table.tblSorter thead tr .headerSortDown, table.tblSorter thead tr .headerSortUp {
background-color: #88c0eb;
}
table.tblSorter tfoot tr {
background-color: #BCD9E8;
}
#filter_form{
float:left;
max-width:69%;
}
#filter_buttons{
float:right;
}
#pagination{
text-align:right;
float:right;
width:30%;
}
.navigation .nav_total{
display:block;
margin-bottom:10px;
}
.navigation .nav_link a, .navigation .nav_current {
border:1px solid #CDCDCD;
margin:0px 2px;
padding:2px 1px;
background:#fff;
text-align:center;
min-width:15px;
display:inline-block;
}
.navigation .nav_current {
border:0px;
background:none;
}
.navigation .nav_first a, .navigation .nav_last a {
padding:2px 2px;
}
.navigation .nav_prev:before, .navigation .nav_next:after {
content:"...";
}
.navigation .nav_link a:hover {
border:1px solid #BCD9E8;
background:#BCD9E8;
text-decoration:none;
}

BIN
images/accept.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

BIN
images/asc.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 B

BIN
images/bg.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

BIN
images/blank.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

BIN
images/cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

BIN
images/chart_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

BIN
images/chart_bar_add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

BIN
images/copy-large.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
images/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

BIN
images/desc.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 B

BIN
images/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

BIN
images/facebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
images/favicon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

BIN
images/filter.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

BIN
images/friendfeed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

BIN
images/pencil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

BIN
images/share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

BIN
images/twitter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/yourls-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

28
includes/auth.php Normal file
View File

@ -0,0 +1,28 @@
<?php
// No direct call
if( !defined( 'YOURLS_ABSPATH' ) ) die();
$auth = yourls_apply_filter( 'is_valid_user', yourls_is_valid_user() );
if( $auth !== true ) {
// API mode,
if ( yourls_is_API() ) {
$format = ( isset($_REQUEST['format']) ? $_REQUEST['format'] : 'xml' );
$callback = ( isset($_REQUEST['callback']) ? $_REQUEST['callback'] : '' );
yourls_api_output( $format, array(
'simple' => $auth,
'message' => $auth,
'errorCode' => 403,
'callback' => $callback,
) );
// Regular mode
} else {
yourls_login_screen( $auth );
}
die();
}
yourls_do_action( 'auth_successful' );

407
includes/class-mysql.php Normal file
View File

@ -0,0 +1,407 @@
<?php
/*
+----------------------------------------------------------------+
| |
| ezSQL |
| Copyright (c) 2006 Justin Vincent |
| |
| File Written By: |
| - Justin Vincent (justin@visunet.ie) |
| - http://php.justinvincent.com |
| |
| File Information: |
| - MYSQL Database Class |
| - class-mysql.php |
| |
+----------------------------------------------------------------+
*/
### ezSQL Constants. Might be defined in the WordPress environment if YOURLS plugin used.
if( !defined('EZSQL_VERSION') )
define('EZSQL_VERSION','2.0');
if( !defined('OBJECT') )
define('OBJECT','OBJECT',true);
if( !defined('ARRAY_A') )
define('ARRAY_A','ARRAY_A',true);
if( !defined('ARRAY_N') )
define('ARRAY_N','ARRAY_N',true);
if( !defined('EZSQL_CORE_ERROR') )
define('EZSQL_CORE_ERROR','ezSQLcore can not be used by itself (it is designed for use by database specific modules).');
### ezSQL Core Class
// Core class containg common functions to manipulate query result sets once returned
class ezSQLcore{
var $trace = false; // same as $debug_all
var $debug_all = false; // same as $trace
var $debug_called = false;
var $vardump_called = false;
var $show_errors = false;
var $num_queries = 0;
var $last_query = null;
var $last_error = null;
var $col_info = null;
var $captured_errors = array();
var $all_queries = '';
## Constructor
function ezSQLcore() { }
## Connect to DB - over-ridden by specific DB class
function connect() { die(EZSQL_CORE_ERROR); }
## Select DB - over-ridden by specific DB class
function select() { die(EZSQL_CORE_ERROR); }
## Basic Query - over-ridden by specific DB class
function query() { die(EZSQL_CORE_ERROR); }
## Format a string correctly for safe insert - over-ridden by specific DB class
function escape() { die(EZSQL_CORE_ERROR); }
## Return database specific system date syntax
function sysdate() { die(EZSQL_CORE_ERROR); }
## Print SQL/DB error - over-ridden by specific DB class
function register_error($err_str) {
// Keep track of last error
$this->last_error = $err_str;
// Capture all errors to an error array no matter what happens
$this->captured_errors[] = array ('error_str' => $err_str, 'query' => $this->last_query);
}
## Show Errors
function show_errors() { $this->show_errors = true; }
## Hide Errors
function hide_errors() { $this->show_errors = false; }
## Kill cached query results
function flush() {
// Get rid of these
$this->last_result = null;
$this->col_info = null;
$this->last_query = null;
}
## Get one variable from the DB - see docs for more detail
function get_var($query=null,$x=0,$y=0) {
// Log how the function was called
$this->func_call = "\$db->get_var(\"$query\",$x,$y)";
// If there is a query then perform it if not then use cached results..
if ($query) {
$this->query($query);
}
// Extract var out of cached results based x,y vals
if ($this->last_result[$y]){
$values = array_values(get_object_vars($this->last_result[$y]));
}
// If there is a value return it else return null
return (isset($values[$x]) && $values[$x]!=='')?$values[$x]:null;
}
## Get one row from the DB - see docs for more detail
function get_row($query=null,$output=OBJECT,$y=0) {
// Log how the function was called
$this->func_call = "\$db->get_row(\"$query\",$output,$y)";
// If there is a query then perform it if not then use cached results..
if ($query) {
$this->query($query);
}
// If the output is an object then return object using the row offset..
if ($output == OBJECT) {
return $this->last_result[$y]?$this->last_result[$y]:null;
// If the output is an associative array then return row as such..
} elseif ($output == ARRAY_A) {
return $this->last_result[$y]?get_object_vars($this->last_result[$y]):null;
// If the output is an numerical array then return row as such..
} elseif ($output == ARRAY_N) {
return $this->last_result[$y]?array_values(get_object_vars($this->last_result[$y])):null;
// If invalid output type was specified..
} else {
$this->print_error(" \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N");
}
}
## Function to get 1 column from the cached result set based in X index see docs for usage and info
function get_col($query=null,$x=0) {
// If there is a query then perform it if not then use cached results..
if ($query) {
$this->query($query);
}
// Extract the column values
for ($i=0; $i < count($this->last_result); $i++) {
$new_array[$i] = $this->get_var(null,$x,$i);
}
return $new_array;
}
## Return the the query as a result set - see docs for more details
function get_results($query=null, $output = OBJECT) {
// Log how the function was called
$this->func_call = "\$db->get_results(\"$query\", $output)";
// If there is a query then perform it if not then use cached results..
if ($query) {
$this->query($query);
}
// Send back array of objects. Each row is an object
if ($output == OBJECT) {
return $this->last_result;
} elseif ($output == ARRAY_A || $output == ARRAY_N) {
if ($this->last_result) {
$i=0;
foreach($this->last_result as $row) {
$new_array[$i] = get_object_vars($row);
if ($output == ARRAY_N) {
$new_array[$i] = array_values($new_array[$i]);
}
$i++;
}
return $new_array;
} else {
return null;
}
}
}
## Function to get column meta data info pertaining to the last query see docs for more info and usage
function get_col_info($info_type="name",$col_offset=-1) {
if ($this->col_info) {
if ($col_offset == -1) {
$i=0;
foreach($this->col_info as $col) {
$new_array[$i] = $col->{$info_type};
$i++;
}
return $new_array;
} else {
return $this->col_info[$col_offset]->{$info_type};
}
}
}
## Dumps the contents of any input variable to screen in a nicely formatted and easy to understand way - any type: Object, Var or Array
function vardump($mixed='') {
echo "<p><table><tr><td bgcolor=ffffff><blockquote><font color=000090>";
echo "<pre><font face=arial>";
if (!$this->vardump_called) {
echo "<font color=800080><b>ezSQL</b> (v".EZSQL_VERSION.") <b>Variable Dump..</b></font>\n\n";
}
$var_type = gettype ($mixed);
print_r(($mixed?$mixed:"<font color=red>No Value / False</font>"));
echo "\n\n<b>Type:</b> " . ucfirst($var_type) . "\n";
echo "<b>Last Query</b> [$this->num_queries]<b>:</b> ".($this->last_query?$this->last_query:"NULL")."\n";
echo "<b>Last Function Call:</b> " . ($this->func_call?$this->func_call:"None")."\n";
echo "<b>Last Rows Returned:</b> ".count($this->last_result)."\n";
echo "</font></pre></font></blockquote></td></tr></table>".$this->donation();
echo "\n<hr size=1 noshade color=dddddd>";
$this->vardump_called = true;
}
## Alias for the above function
function dumpvar($mixed) { $this->vardump($mixed); }
## Displays the last query string that was sent to the database & a table listing results (if there were any). (abstracted into a seperate file to save server overhead).
function debug() {
echo "<blockquote>";
// Only show ezSQL credits once..
if (!$this->debug_called) {
echo "<font color=800080 face=arial size=2><b>ezSQL</b> (v".EZSQL_VERSION.") <b>Debug..</b></font><p>\n";
}
if ($this->last_error) {
echo "<font face=arial size=2 color=000099><b>Last Error --</b> [<font color=000000><b>$this->last_error</b></font>]<p>";
}
echo "<font face=arial size=2 color=000099><b>Query</b> [$this->num_queries] <b>--</b> ";
echo "[<font color=000000><b>$this->last_query</b></font>]</font><p>";
echo "<font face=arial size=2 color=000099><b>Query Result..</b></font>";
echo "<blockquote>";
if ($this->col_info) {
// Results top rows
echo "<table cellpadding=5 cellspacing=1 bgcolor=555555>";
echo "<tr bgcolor=eeeeee><td nowrap valign=bottom><font color=555599 face=arial size=2><b>(row)</b></font></td>";
for ($i=0; $i < count($this->col_info); $i++) {
echo "<td nowrap align=left valign=top><font size=1 color=555599 face=arial>{$this->col_info[$i]->type} {$this->col_info[$i]->max_length}</font><br><span style='font-family: arial; font-size: 10pt; font-weight: bold;'>{$this->col_info[$i]->name}</span></td>";
}
echo "</tr>";
// print main results
if ($this->last_result) {
$i=0;
foreach ($this->get_results(null,ARRAY_N) as $one_row) {
$i++;
echo "<tr bgcolor=ffffff><td bgcolor=eeeeee nowrap align=middle><font size=2 color=555599 face=arial>$i</font></td>";
foreach ($one_row as $item) {
echo "<td nowrap><font face=arial size=2>$item</font></td>";
}
echo "</tr>";
}
} else {
echo "<tr bgcolor=ffffff><td colspan=".(count($this->col_info)+1)."><font face=arial size=2>No Results</font></td></tr>";
}
echo "</table>";
} else {
echo "<font face=arial size=2>No Results</font>";
}
echo "</blockquote></blockquote>".$this->donation()."<hr noshade color=dddddd size=1>";
$this->debug_called = true;
}
## Naughty little function to ask for some remuniration!
function donation() {
return "<font size=1 face=arial color=000000>If ezSQL has helped <a href=\"https://www.paypal.com/xclick/business=justin%40justinvincent.com&item_name=ezSQL&no_note=1&tax=0\" style=\"color: 0000CC;\">make a donation!?</a> &nbsp;&nbsp;<!--[ go on! you know you want to! ]--></font>";
}
}
### ezSQL MYSQL Class Variables
$ezsql_mysql_str = array
(
1 => 'Require $dbuser and $dbpassword to connect to a database server',
2 => 'Error establishing mySQL database connection. Correct user/password? Correct hostname? Database server running?',
3 => 'Require $dbname to select a database',
4 => 'mySQL database connection is not active',
5 => 'Unexpected error while trying to select database'
);
if ( ! function_exists ('mysql_connect') ) die('<b>Fatal Error:</b> ezSQL_mysql requires mySQL Lib to be compiled and or linked in to the PHP engine');
if ( ! class_exists ('ezSQLcore') ) die('<b>Fatal Error:</b> ezSQL_mysql requires ezSQLcore (ez_sql_core.php) to be included/loaded before it can be used');
### ezSQL MYSQL Class
class ezSQL_mysql extends ezSQLcore {
## Constructor - allow the user to perform a qucik connect at the same time as initialising the ezSQL_mysql class
function ezSQL_mysql($dbuser='', $dbpassword='', $dbname='', $dbhost='localhost') {
if ($dbuser && $dbname) {
$this->quick_connect($dbuser, $dbpassword, $dbname, $dbhost);
}
}
## Short hand way to connect to mySQL database server and select a mySQL database at the same time
function quick_connect($dbuser='', $dbpassword='', $dbname='', $dbhost='localhost') {
$return_val = false;
if ( ! $this->connect($dbuser, $dbpassword, $dbhost,true) ) ;
else if ( ! $this->select($dbname) ) ;
else $return_val = true;
return $return_val;
}
## Try to connect to mySQL database server
function connect($dbuser='', $dbpassword='', $dbhost='localhost'){
global $ezsql_mysql_str; $return_val = false;
// Must have a user and a password
if (!$dbuser){
$this->register_error($ezsql_mysql_str[1].' in '.__FILE__.' on line '.__LINE__);
$this->show_errors ? trigger_error($ezsql_mysql_str[1],E_USER_WARNING) : null;
// Try to establish the server database handle
} else if (!$this->dbh = @mysql_connect($dbhost,$dbuser,$dbpassword)) {
$this->register_error($ezsql_mysql_str[2].' in '.__FILE__.' on line '.__LINE__);
$this->show_errors ? trigger_error($ezsql_mysql_str[2],E_USER_WARNING) : null;
} else {
$return_val = true;
}
return $return_val;
}
## Close
function close() {
return mysql_close($this->dbh);
}
## Try to select a mySQL database
function select($dbname='') {
global $ezsql_mysql_str; $return_val = false;
// Must have a database name
if (!$dbname) {
$this->register_error($ezsql_mysql_str[3].' in '.__FILE__.' on line '.__LINE__);
$this->show_errors ? trigger_error($ezsql_mysql_str[3],E_USER_WARNING) : null;
// Must have an active database connection
} else if (!$this->dbh) {
$this->register_error($ezsql_mysql_str[4].' in '.__FILE__.' on line '.__LINE__);
$this->show_errors ? trigger_error($ezsql_mysql_str[4],E_USER_WARNING) : null;
// Try to connect to the database
} else if (!@mysql_select_db($dbname,$this->dbh)) {
// Try to get error supplied by mysql if not use our own
if ( !$str = @mysql_error($this->dbh)) {
$str = $ezsql_mysql_str[5];
}
$this->register_error($str.' in '.__FILE__.' on line '.__LINE__);
$this->show_errors ? trigger_error($str,E_USER_WARNING) : null;
} else {
$return_val = true;
}
return $return_val;
}
## Format a mySQL string correctly for safe mySQL insert (no mater if magic quotes are on or not)
function escape($str) {
return mysql_escape_string(stripslashes($str));
}
## Return mySQL specific system date syntax
function sysdate() {
return 'NOW()';
}
## Perform mySQL query and try to detirmin result value
function query($query) {
// For reg expressions
$query = trim($query);
// Initialise return
$return_val = 0;
// Flush cached values..
$this->flush();
// Log how the function was called
$this->func_call = "\$db->query(\"$query\")";
// Keep track of the last query for debug..
$this->last_query = $query;
// Perform the query via std mysql_query function..
$this->result = @mysql_query($query,$this->dbh);
$this->num_queries++;
$this->all_queries .= $query.'<br />';
// If there is an error then take note of it..
if ($str = @mysql_error($this->dbh)) {
$this->register_error($str);
$this->show_errors ? trigger_error($str,E_USER_WARNING) : null;
return false;
}
// Query was an insert, delete, update, replace
if (preg_match("/^(insert|delete|update|replace)\s+/i",$query)) {
$this->rows_affected = @mysql_affected_rows();
// Take note of the insert_id
if (preg_match("/^(insert|replace)\s+/i",$query)) {
$this->insert_id = @mysql_insert_id($this->dbh);
}
// Return number fo rows affected
$return_val = $this->rows_affected;
// Query was a select
} else {
// Take note of column info
$i=0;
while ($i < @mysql_num_fields($this->result)) {
$this->col_info[$i] = @mysql_fetch_field($this->result);
$i++;
}
// Store Query Results
$num_rows=0;
while ($row = @mysql_fetch_object($this->result)) {
// Store relults as an objects within main array
$this->last_result[$num_rows] = $row;
$num_rows++;
}
@mysql_free_result($this->result);
// Log number of rows the query returned
$this->num_rows = $num_rows;
// Return number of rows selected
$return_val = $this->num_rows;
}
// If debug ALL queries
$this->trace || $this->debug_all ? $this->debug() : null ;
return $return_val;
}
function mysql_version() {
return mysql_get_server_info( $this->dbh ) ;
}
}
?>

195
includes/functions-auth.php Normal file
View File

@ -0,0 +1,195 @@
<?php
// Check for valid user. Returns true or an error message
function yourls_is_valid_user() {
static $valid = false;
if( $valid )
return true;
// Logout request
if( isset( $_GET['action'] ) && $_GET['action'] == 'logout') {
yourls_store_cookie( null );
return 'Logged out successfully';
}
// Check cookies or login request. Login form has precedence.
global $yourls_user_passwords;
// Determine auth method and check credentials
if
// API only: Secure (no login or pwd) and time limited token
// ?timestamp=12345678&signature=md5(totoblah12345678)
( yourls_is_API() &&
isset($_REQUEST['timestamp']) && !empty($_REQUEST['timestamp']) &&
isset($_REQUEST['signature']) && !empty($_REQUEST['signature'])
)
{
$valid = yourls_check_signature_timestamp();
}
elseif
// API only: Secure (no login or pwd)
// ?signature=md5(totoblah)
( yourls_is_API() &&
!isset($_REQUEST['timestamp']) &&
isset($_REQUEST['signature']) && !empty($_REQUEST['signature'])
)
{
$valid = yourls_check_signature();
}
elseif
// API or normal: login with username & pwd
( isset($_REQUEST['username']) && isset($_REQUEST['password'])
&& !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password'] ) )
{
$valid = yourls_check_username_password();
}
elseif
// Normal only: cookies
( !yourls_is_API() &&
isset($_COOKIE['yourls_username']) && isset($_COOKIE['yourls_password']) )
{
$valid = yourls_check_auth_cookie();
}
// Login for the win!
if ( $valid ) {
// (Re)store encrypted cookie and tell it's ok
if ( !yourls_is_API() ) // No need to store a cookie when used in API mode.
yourls_store_cookie( YOURLS_USER );
return true;
}
// Login failed
if ( isset($_REQUEST['username']) || isset($_REQUEST['password']) ) {
return 'Invalid username or password';
} else {
return 'Please log in';
}
}
// Check auth against list of login=>pwd. Sets user if applicable, returns bool
function yourls_check_username_password() {
global $yourls_user_passwords;
if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $yourls_user_passwords[ $_REQUEST['username'] ], $_REQUEST['password'] ) ) {
yourls_set_user( $_REQUEST['username'] );
return true;
}
return false;
}
// Check a REQUEST password sent in plain text against stored password which can be a salted hash
function yourls_check_password_hash( $stored, $plaintext ) {
if ( substr( $stored, 0, 4 ) == 'md5:' and strlen( $stored ) == 42 ) {
// Stored password is a salted hash: "md5:<$r = rand(10000,99999)>:<md5($r.'thepassword')>"
// And 42. Of course. http://www.google.com/search?q=the+answer+to+life+the+universe+and+everything
list( $temp, $salt, $md5 ) = split( ':', $stored );
return( $stored == 'md5:'.$salt.':'.md5( $salt.$plaintext ) );
} else {
// Password was sent in clear
return( $stored == $plaintext );
}
}
// Check auth against encrypted COOKIE data. Sets user if applicable, returns bool
function yourls_check_auth_cookie() {
global $yourls_user_passwords;
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
if(
yourls_salt($valid_user) == $_COOKIE['yourls_username']
&& yourls_salt($valid_password) == $_COOKIE['yourls_password']
) {
yourls_set_user( $valid_user );
return true;
}
}
return false;
}
// Check auth against signature and timestamp. Sets user if applicable, returns bool
function yourls_check_signature_timestamp() {
// Timestamp in PHP : time()
// Timestamp in JS: parseInt(new Date().getTime() / 1000)
global $yourls_user_passwords;
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
if (
(
md5( $_REQUEST['timestamp'].yourls_auth_signature( $valid_user ) ) == $_REQUEST['signature']
or
md5( yourls_auth_signature( $valid_user ).$_REQUEST['timestamp'] ) == $_REQUEST['signature']
)
&&
yourls_check_timestamp( $_REQUEST['timestamp'] )
) {
yourls_set_user( $valid_user );
return true;
}
}
return false;
}
// Check auth against signature. Sets user if applicable, returns bool
function yourls_check_signature() {
global $yourls_user_passwords;
foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
if ( yourls_auth_signature( $valid_user ) == $_REQUEST['signature'] ) {
yourls_set_user( $valid_user );
return true;
}
}
return false;
}
// Generate secret signature hash
function yourls_auth_signature( $username = false ) {
if( !$username && defined('YOURLS_USER') ) {
$username = YOURLS_USER;
}
return ( $username ? substr( yourls_salt( $username ), 0, 10 ) : 'Cannot generate auth signature: no username' );
}
// Check a timestamp is from the past and not too old
function yourls_check_timestamp( $time ) {
$now = time();
return ( $now >= $time && ceil( $now - $time ) < YOURLS_NONCE_LIFE );
}
// Store new cookie. No $user will delete the cookie.
function yourls_store_cookie( $user = null ) {
if( !$user ) {
$pass = null;
$time = time() - 3600;
} else {
global $yourls_user_passwords;
if( isset($yourls_user_passwords[$user]) ) {
$pass = $yourls_user_passwords[$user];
} else {
die('Stealing cookies?'); // This should never happen
}
$time = time() + YOURLS_COOKIE_LIFE;
}
$domain = yourls_apply_filter( 'setcookie_domain', parse_url( YOURLS_SITE, 1 ) );
$secure = yourls_apply_filter( 'setcookie_secure', yourls_is_ssl() );
$httponly = yourls_apply_filter( 'setcookie_httponly', true );
if ( !headers_sent() ) {
// Set httponly if the php version is >= 5.2.0
if( version_compare( phpversion(), '5.2.0', 'ge' ) ) {
setcookie('yourls_username', yourls_salt( $user ), $time, '/', $domain, $secure, $httponly );
setcookie('yourls_password', yourls_salt( $pass ), $time, '/', $domain, $secure, $httponly );
} else {
setcookie('yourls_username', yourls_salt( $user ), $time, '/', $domain, $secure );
setcookie('yourls_password', yourls_salt( $pass ), $time, '/', $domain, $secure );
}
}
}
// Set user name
function yourls_set_user( $user ) {
if( !defined('YOURLS_USER') )
define('YOURLS_USER', $user);
}

View File

@ -0,0 +1,151 @@
<?php
// json_encode for PHP prior to 5.2
if( !function_exists( 'json_encode' ) ) {
function json_encode( $array ) {
return yourls_array_to_json( $array );
}
}
/**
* Converts an associative array of arbitrary depth and dimension into JSON representation. Used for compatibility with older PHP builds.
*
* @param $array The array to convert.
* @return mixed The resulting JSON string, or false if the argument was not an array.
* @author Andy Rusterholz
* @link http://php.net/json_encode (see comments)
*/
function yourls_array_to_json( $array ){
if( !is_array( $array ) ){
return false;
}
$associative = count( array_diff( array_keys($array), array_keys( array_keys( $array )) ));
if( $associative ){
$construct = array();
foreach( $array as $key => $value ){
// We first copy each key/value pair into a staging array,
// formatting each key and value properly as we go.
// Format the key:
if( is_numeric($key) ){
$key = "key_$key";
}
$key = '"'.addslashes($key).'"';
// Format the value:
if( is_array( $value )){
$value = yourls_array_to_json( $value );
} else if( !is_numeric( $value ) || is_string( $value ) ){
$value = '"'.addslashes($value).'"';
}
// Add to staging array:
$construct[] = "$key: $value";
}
// Then we collapse the staging array into the JSON form:
$result = "{ " . implode( ", ", $construct ) . " }";
} else { // If the array is a vector (not associative):
$construct = array();
foreach( $array as $value ){
// Format the value:
if( is_array( $value )){
$value = yourls_array_to_json( $value );
} else if( !is_numeric( $value ) || is_string( $value ) ){
$value = '"'.addslashes($value).'"';
}
// Add to staging array:
$construct[] = $value;
}
// Then we collapse the staging array into the JSON form:
$result = "[ " . implode( ", ", $construct ) . " ]";
}
return $result;
}
// Compat http_build_query for PHP4
if (!function_exists('http_build_query')) {
function http_build_query($data, $prefix=null, $sep=null) {
return yourls_http_build_query($data, $prefix, $sep);
}
}
// from php.net (modified by Mark Jaquith to behave like the native PHP5 function)
function yourls_http_build_query($data, $prefix=null, $sep=null, $key='', $urlencode=true) {
$ret = array();
foreach ( (array) $data as $k => $v ) {
if ( $urlencode)
$k = urlencode($k);
if ( is_int($k) && $prefix != null )
$k = $prefix.$k;
if ( !empty($key) )
$k = $key . '%5B' . $k . '%5D';
if ( $v === NULL )
continue;
elseif ( $v === FALSE )
$v = '0';
if ( is_array($v) || is_object($v) )
array_push($ret,yourls_http_build_query($v, '', $sep, $k, $urlencode));
elseif ( $urlencode )
array_push($ret, $k.'='.urlencode($v));
else
array_push($ret, $k.'='.$v);
}
if ( NULL === $sep )
$sep = ini_get('arg_separator.output');
return implode($sep, $ret);
}
// htmlspecialchars_decode for PHP < 5.1
if ( !function_exists('htmlspecialchars_decode') ) {
function htmlspecialchars_decode($text) {
return strtr($text, array_flip(get_html_translation_table(HTML_SPECIALCHARS)));
}
}
// BC Math functions (assuming if one doesn't exist, none does)
if ( !function_exists( 'bcdiv' )) {
function bcdiv( $dividend, $divisor ) {
$quotient = floor( $dividend/$divisor );
return $quotient;
}
function bcmod( $dividend, $modulo ) {
$remainder = $dividend%$modulo;
return $remainder;
}
function bcmul( $left, $right ) {
return $left * $right;
}
function bcadd( $left, $right ) {
return $left + $right;
}
function bcpow( $base, $power ) {
return pow( $base, $power );
}
}
// Replacement for property_exists() (5.1+)
if ( !function_exists( 'property_exists' ) ) {
function property_exists( $class, $property ) {
if ( is_object( $class ) ) {
$vars = get_object_vars( $class );
} else {
$vars = get_class_vars( $class );
}
return array_key_exists( $property, $vars );
}
}

View File

@ -0,0 +1,211 @@
<?php
/*
* YOURLS
* Function library for anything related to formatting / validating / sanitizing
*/
// function to convert an integer (1337) to a string (3jk).
function yourls_int2string( $num, $chars = null ) {
if( $chars == null )
$chars = yourls_get_shorturl_charset();
$string = '';
$len = strlen( $chars );
while( $num >= $len ) {
$mod = bcmod( $num, $len );
$num = bcdiv( $num, $len );
$string = $chars[$mod] . $string;
}
$string = $chars[$num] . $string;
return yourls_apply_filter( 'int2string', $string, $num, $chars );
}
// function to convert a string (3jk) to an integer (1337)
function yourls_string2int( $string, $chars = null ) {
if( $chars == null )
$chars = yourls_get_shorturl_charset();
$integer = 0;
$string = strrev( $string );
$baselen = strlen( $chars );
$inputlen = strlen( $string );
for ($i = 0; $i < $inputlen; $i++) {
$index = strpos( $chars, $string[$i] );
$integer = bcadd( $integer, bcmul( $index, bcpow( $baselen, $i ) ) );
}
return yourls_apply_filter( 'string2int', $integer, $string, $chars );
}
// return a unique(ish) hash for a string to be used as a valid HTML id
function yourls_string2htmlid( $string ) {
return yourls_apply_filter( 'string2htmlid', 'y'.abs( crc32( $string ) ) );
}
// Make sure a link keyword (ie "1fv" as in "site.com/1fv") is valid.
function yourls_sanitize_string( $string ) {
// make a regexp pattern with the shorturl charset, and remove everything but this
$pattern = yourls_make_regexp_pattern( yourls_get_shorturl_charset() );
$valid = substr(preg_replace('![^'.$pattern.']!', '', $string ), 0, 199);
return yourls_apply_filter( 'sanitize_string', $valid, $string );
}
// Alias function. I was always getting it wrong.
function yourls_sanitize_keyword( $keyword ) {
return yourls_sanitize_string( $keyword );
}
// Sanitize a page title. No HTML per W3C http://www.w3.org/TR/html401/struct/global.html#h-7.4.2
function yourls_sanitize_title( $title ) {
// TODO: make stronger Implement KSES?
$title = strip_tags( $title );
// Remove extra white space
$title = preg_replace( "/\s+/", ' ', trim( $title ) );
return $title;
}
// A few sanity checks on the URL
function yourls_sanitize_url( $url, $force_protocol = true, $force_lowercase = true ) {
// make sure there's only one 'http://' at the beginning (prevents pasting a URL right after the default 'http://')
$url = str_replace(
array( 'http://http://', 'http://https://' ),
array( 'http://', 'https://' ),
$url
);
if( $force_protocol ) {
// make sure there's a protocol, add http:// if not
if ( !preg_match('!^([a-zA-Z]+://)!', $url ) )
$url = 'http://'.$url;
}
if( $force_lowercase ) {
// force scheme and domain to lowercase - see issue 591
preg_match( '!^([a-zA-Z]+://([^/]+))(.*)$!', $url, $matches );
if( isset( $matches[1] ) && isset( $matches[3] ) )
$url = strtolower( $matches[1] ) . $matches[3];
}
// clean and shave
$url = yourls_clean_url( $url );
return substr( $url, 0, 1999 );
}
// Function to filter all invalid characters from a URL. Stolen from WP's clean_url()
function yourls_clean_url( $url ) {
$url = preg_replace( '|[^a-z0-9-~+_.?\[\]\^#=!&;,/:%@$\|*\'"()\\x80-\\xff]|i', '', $url );
$strip = array( '%0d', '%0a', '%0D', '%0A' );
$url = yourls_deep_replace( $strip, $url );
$url = str_replace( ';//', '://', $url );
$url = str_replace( '&amp;', '&', $url ); // Revert & not to break query strings
return $url;
}
// Perform a replacement while a string is found, eg $subject = '%0%0%0DDD', $search ='%0D' -> $result =''
// Stolen from WP's _deep_replace
function yourls_deep_replace($search, $subject){
$found = true;
while($found) {
$found = false;
foreach( (array) $search as $val ) {
while(strpos($subject, $val) !== false) {
$found = true;
$subject = str_replace($val, '', $subject);
}
}
}
return $subject;
}
// Make sure an integer is a valid integer (PHP's intval() limits to too small numbers)
// TODO FIXME FFS: unused ?
function yourls_sanitize_int($in) {
return ( substr(preg_replace('/[^0-9]/', '', strval($in) ), 0, 20) );
}
// Make sure a integer is safe
// Note: this is not checking for integers, since integers on 32bits system are way too limited
// TODO: find a way to validate as integer
function yourls_intval($in) {
return yourls_escape($in);
}
// Escape a string
function yourls_escape( $in ) {
return mysql_real_escape_string($in);
}
// Sanitize an IP address
function yourls_sanitize_ip( $ip ) {
return preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip );
}
// Make sure a date is m(m)/d(d)/yyyy, return false otherwise
function yourls_sanitize_date( $date ) {
if( !preg_match( '!^\d{1,2}/\d{1,2}/\d{4}$!' , $date ) ) {
return false;
}
return $date;
}
// Sanitize a date for SQL search. Return false if malformed input.
function yourls_sanitize_date_for_sql( $date ) {
if( !yourls_sanitize_date( $date ) )
return false;
return date('Y-m-d', strtotime( $date ) );
}
// Return word or words if more than one
function yourls_plural( $word, $count=1 ) {
return $word . ($count > 1 ? 's' : '');
}
// Return trimmed string
function yourls_trim_long_string( $string, $length = 60, $append = '[...]' ) {
$newstring = $string;
if( function_exists('mb_substr') ) {
if ( mb_strlen( $newstring ) > $length ) {
$newstring = mb_substr( $newstring, 0, $length - mb_strlen( $append ), 'UTF-8' ) . $append;
}
} else {
if ( strlen( $newstring ) > $length ) {
$newstring = substr( $newstring, 0, $length - strlen( $append ) ) . $append;
}
}
return yourls_apply_filter( 'trim_long_string', $newstring, $string, $length, $append );
}
// Sanitize a version number (1.4.1-whatever -> 1.4.1)
function yourls_sanitize_version( $ver ) {
return preg_replace( '/[^0-9.]/', '', $ver );
}
// Sanitize a filename (no Win32 stuff)
function yourls_sanitize_filename( $file ) {
$file = str_replace( '\\', '/', $file ); // sanitize for Win32 installs
$file = preg_replace( '|/+|' ,'/', $file ); // remove any duplicate slash
return $file;
}
// Check if a string seems to be UTF-8. Stolen from WP.
function yourls_seems_utf8($str) {
$length = strlen($str);
for ($i=0; $i < $length; $i++) {
$c = ord($str[$i]);
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}

626
includes/functions-html.php Normal file
View File

@ -0,0 +1,626 @@
<?php
// Display <h1> header and logo
function yourls_html_logo() {
yourls_do_action( 'pre_html_logo' );
?>
<h1>
<a href="<?php echo yourls_admin_url('index.php') ?>" title="YOURLS"><span>YOURLS</span>: <span>Y</span>our <span>O</span>wn <span>URL</span> <span>S</span>hortener<br/>
<img src="<?php yourls_site_url(); ?>/images/yourls-logo.png" alt="YOURLS" title="YOURLS" border="0" style="border: 0px;" /></a>
</h1>
<?php
yourls_do_action( 'html_logo' );
}
// Display HTML head and <body> tag
function yourls_html_head( $context = 'index', $title = '' ) {
yourls_do_action( 'pre_html_head', $context, $title );
// All components to false, except when specified true
$share = $insert = $tablesorter = $tabs = $cal = false;
// Load components as needed
switch ( $context ) {
case 'infos':
$share = $tabs = true;
break;
case 'bookmark':
$share = $insert = $tablesorter = true;
break;
case 'index':
$insert = $tablesorter = $cal = $share = true;
break;
case 'plugins':
case 'tools':
$tablesorter = true;
break;
case 'install':
case 'login':
case 'new':
case 'upgrade':
break;
}
// Force no cache for all admin pages
if( yourls_is_admin() && !headers_sent() ) {
header( 'Expires: Thu, 23 Mar 1972 07:00:00 GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
header( 'Pragma: no-cache' );
yourls_do_action( 'admin_headers', $context, $title );
}
// Store page context in global object
global $ydb;
$ydb->context = $context;
// Body class
$bodyclass = yourls_apply_filter( 'bodyclass', '' );
$bodyclass .= ( yourls_is_mobile_device() ? 'mobile' : 'desktop' );
// Page title
$_title = 'YOURLS &mdash; Your Own URL Shortener | ' . yourls_link();
$title = $title ? $title . " &laquo; " . $_title : $_title;
$title = yourls_apply_filter( 'html_title', $title, $context );
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><?php echo $title ?></title>
<link rel="icon" type="image/gif" href="<?php yourls_site_url(); ?>/images/favicon.gif" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="author" content="Ozh RICHARD & Lester CHAN for http://yourls.org/" />
<meta name="generator" content="YOURLS <?php echo YOURLS_VERSION ?>" />
<meta name="description" content="Insert URL &laquo; YOURLS &raquo; Your Own URL Shortener' | <?php yourls_site_url(); ?>" />
<script src="<?php yourls_site_url(); ?>/js/jquery-1.6.1.min.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<script src="<?php yourls_site_url(); ?>/js/common.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<script src="<?php yourls_site_url(); ?>/js/jquery.notifybar.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<link rel="stylesheet" href="<?php yourls_site_url(); ?>/css/style.css?v=<?php echo YOURLS_VERSION; ?>" type="text/css" media="screen" />
<?php if ($tabs) { ?>
<link rel="stylesheet" href="<?php yourls_site_url(); ?>/css/infos.css?v=<?php echo YOURLS_VERSION; ?>" type="text/css" media="screen" />
<script src="<?php yourls_site_url(); ?>/js/infos.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<?php } ?>
<?php if ($tablesorter) { ?>
<link rel="stylesheet" href="<?php yourls_site_url(); ?>/css/tablesorter.css?v=<?php echo YOURLS_VERSION; ?>" type="text/css" media="screen" />
<script src="<?php yourls_site_url(); ?>/js/jquery.tablesorter.min.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<?php } ?>
<?php if ($insert) { ?>
<script src="<?php yourls_site_url(); ?>/js/insert.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<?php } ?>
<?php if ($share) { ?>
<link rel="stylesheet" href="<?php yourls_site_url(); ?>/css/share.css?v=<?php echo YOURLS_VERSION; ?>" type="text/css" media="screen" />
<script src="<?php yourls_site_url(); ?>/js/share.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<script src="<?php yourls_site_url(); ?>/js/jquery.zclip.min.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<?php } ?>
<?php if ($cal) { ?>
<link rel="stylesheet" href="<?php yourls_site_url(); ?>/css/cal.css?v=<?php echo YOURLS_VERSION; ?>" type="text/css" media="screen" />
<script src="<?php yourls_site_url(); ?>/js/jquery.cal.js?v=<?php echo YOURLS_VERSION; ?>" type="text/javascript"></script>
<?php } ?>
<script type="text/javascript">
//<![CDATA[
var ajaxurl = '<?php echo yourls_admin_url( 'admin-ajax.php' ); ?>';
var zclipurl = '<?php yourls_site_url(); ?>/js/ZeroClipboard.swf';
//]]>
</script>
<?php yourls_do_action( 'html_head', $context ); ?>
</head>
<body class="<?php echo $context; ?> <?php echo $bodyclass; ?>">
<div id="wrap">
<?php
}
// Display HTML footer (including closing body & html tags)
function yourls_html_footer() {
global $ydb;
$num_queries = $ydb->num_queries > 1 ? $ydb->num_queries.' queries' : $ydb->num_queries.' query';
?>
</div> <?php // wrap ?>
<div id="footer"><p>Powered by <a href="http://yourls.org/" title="YOURLS">YOURLS</a> v<?php echo YOURLS_VERSION; echo ' &ndash; '.$num_queries; ?></p></div>
<?php if( defined('YOURLS_DEBUG') && YOURLS_DEBUG == true ) {
echo '<p>'. $ydb->all_queries .'<p>';
} ?>
<?php yourls_do_action( 'html_footer', $ydb->context ); ?>
</body>
</html>
<?php
}
// Display "Add new URL" box
function yourls_html_addnew( $url = '', $keyword = '' ) {
$url = $url ? $url : 'http://';
?>
<div id="new_url">
<div>
<form id="new_url_form" action="" method="get">
<div><strong>Enter the URL</strong>:<input type="text" id="add-url" name="url" value="<?php echo $url; ?>" class="text" size="80" />
Optional: <strong>Custom short URL</strong>:<input type="text" id="add-keyword" name="keyword" value="<?php echo $keyword; ?>" class="text" size="8" />
<?php yourls_nonce_field( 'add_url', 'nonce-add' ); ?>
<input type="button" id="add-button" name="add-button" value="Shorten The URL" class="button" onclick="add();" /></div>
</form>
<div id="feedback" style="display:none"></div>
</div>
<?php yourls_do_action( 'html_addnew' ); ?>
</div>
<?php
}
// Display main table's footer
function yourls_html_tfooter( $params = array() ) {
extract( $params ); // extract $search_text, $page, $search_in_sql ...
?>
<tfoot>
<tr>
<th colspan="6">
<div id="filter_form">
<form action="" method="get">
<div id="filter_options">
Search&nbsp;for&nbsp;
<input type="text" name="s_search" class="text" size="15" value="<?php echo $search_text; ?>" />
&nbsp;in&nbsp;
<select name="s_in" size="1">
<option value="keyword"<?php if($search_in_sql == 'keyword') { echo ' selected="selected"'; } ?>>Short URL</option>
<option value="url"<?php if($search_in_sql == 'url') { echo ' selected="selected"'; } ?>>URL</option>
<option value="title"<?php if($search_in_sql == 'title') { echo ' selected="selected"'; } ?>>Title</option>
<option value="ip"<?php if($search_in_sql == 'ip') { echo ' selected="selected"'; } ?>>IP</option>
</select>
&ndash;&nbsp;Order&nbsp;by&nbsp;
<select name="s_by" size="1">
<option value="id"<?php if($sort_by_sql == 'keyword') { echo ' selected="selected"'; } ?>>Short URL</option>
<option value="url"<?php if($sort_by_sql == 'url') { echo ' selected="selected"'; } ?>>URL</option>
<option value="timestamp"<?php if($sort_by_sql == 'timestamp') { echo ' selected="selected"'; } ?>>Date</option>
<option value="ip"<?php if($sort_by_sql == 'ip') { echo ' selected="selected"'; } ?>>IP</option>
<option value="clicks"<?php if($sort_by_sql == 'clicks') { echo ' selected="selected"'; } ?>>Clicks</option>
</select>
<select name="s_order" size="1">
<option value="asc"<?php if($sort_order_sql == 'asc') { echo ' selected="selected"'; } ?>>Ascending</option>
<option value="desc"<?php if($sort_order_sql == 'desc') { echo ' selected="selected"'; } ?>>Descending</option>
</select>
&ndash;&nbsp;Show&nbsp;
<input type="text" name="perpage" class="text" size="2" value="<?php echo $perpage; ?>" />&nbsp;rows<br/>
Show links with
<select name="link_filter" size="1">
<option value="more"<?php if($link_filter === 'more') { echo ' selected="selected"'; } ?>>more</option>
<option value="less"<?php if($link_filter === 'less') { echo ' selected="selected"'; } ?>>less</option>
</select>
than
<input type="text" name="link_limit" class="text" size="4" value="<?php echo $link_limit; ?>" />clicks<br/>
Show links created
<select name="date_filter" id="date_filter" size="1">
<option value="before"<?php if($date_filter === 'before') { echo ' selected="selected"'; } ?>>before</option>
<option value="after"<?php if($date_filter === 'after') { echo ' selected="selected"'; } ?>>after</option>
<option value="between"<?php if($date_filter === 'between') { echo ' selected="selected"'; } ?>> between</option>
</select>
<input type="text" name="date_first" id="date_first" class="text" size="12" value="<?php echo $date_first; ?>" />
<span id="date_and" <?php if($date_filter === 'between') { echo ' style="display:inline"'; } ?>> and </span>
<input type="text" name="date_second" id="date_second" class="text" size="12" value="<?php echo $date_second; ?>" <?php if($date_filter === 'between') { echo ' style="display:inline"'; } ?>/>
<div id="filter_buttons">
<input type="submit" id="submit-sort" value="Search" class="button primary" />
&nbsp;
<input type="button" id="submit-clear-filter" value="Clear" class="button" onclick="window.parent.location.href = 'index.php'" />
</div>
</div>
</form>
</div>
<div id="pagination">
<span class="navigation">
<?php if( $total_pages > 1 ) { ?>
<span class="nav_total"><?php echo $total_pages .' '. yourls_plural( 'page', $total_pages ); ?></span>
<?php
$base_page = yourls_admin_url( 'index.php' );
// Pagination offsets: min( max ( zomg! ) );
$p_start = max( min( $total_pages - 4, $page - 2 ), 1 );
$p_end = min( max( 5, $page + 2 ), $total_pages );
if( $p_start >= 2 ) {
$link = yourls_add_query_arg( array_merge( $params, array( 'page' => 1 ) ), $base_page );
echo '<span class="nav_link nav_first"><a href="' . $link . '" title="Go to First Page">&laquo; First</a></span>';
echo '<span class="nav_link nav_prev"></span>';
}
for( $i = $p_start ; $i <= $p_end; $i++ ) {
if( $i == $page ) {
echo "<span class='nav_link nav_current'>$i</span>";
} else {
$link = yourls_add_query_arg( array_merge( $params, array( 'page' => $i ) ), $base_page );
echo '<span class="nav_link nav_goto"><a href="' . $link . '" title="Page '.$i.'">'.$i.'</a></span>';
}
}
if( ( $p_end ) < $total_pages ) {
$link = yourls_add_query_arg( array_merge( $params, array( 'page' => $total_pages ) ), $base_page );
echo '<span class="nav_link nav_next"></span>';
echo '<span class="nav_link nav_last"><a href="' . $link . '" title="Go to Last Page">Last &raquo;</a></span>';
}
?>
<?php } ?>
</span>
</div>
</th>
</tr>
<?php yourls_do_action( 'html_tfooter' ); ?>
</tfoot>
<?php
}
// Display the Quick Share box
function yourls_share_box( $longurl, $shorturl, $title='', $text='', $shortlink_title = '<h2>Your short link</h2>', $share_title = '<h2>Quick Share</h2>', $hidden = false ) {
// Allow plugins to short-circuit the whole function
$pre = yourls_apply_filter( 'shunt_share_box', false );
if ( false !== $pre )
return $pre;
$text = ( $text ? '"'.$text.'" ' : '' );
$title = ( $title ? "$title " : '' );
$share = htmlspecialchars_decode( $title.$text.$shorturl );
$count = 140 - strlen( $share );
$hidden = ( $hidden ? 'style="display:none;"' : '' );
// Allow plugins to filter all data
$data = compact( 'longurl', 'shorturl', 'title', 'text', 'shortlink_title', 'share_title', 'share', 'count', 'hidden' );
$data = yourls_apply_filter( 'share_box_data', $data );
extract( $data );
$_share = rawurlencode( $share );
$_url = rawurlencode( $shorturl );
?>
<div id="shareboxes" <?php echo $hidden; ?>>
<?php yourls_do_action( 'shareboxes_before', $longurl, $shorturl, $title, $text ); ?>
<div id="copybox" class="share">
<?php echo $shortlink_title; ?>
<p><input id="copylink" class="text" size="32" value="<?php echo $shorturl; ?>" /></p>
<p><small>Long link: <a id="origlink" href="<?php echo $longurl; ?>"><?php echo $longurl; ?></a></small>
<?php if( yourls_do_log_redirect() ) { ?>
<br/><small>Stats: <a id="statlink" href="<?php echo $shorturl; ?>+"><?php echo $shorturl; ?>+</a></small>
<input type="hidden" id="titlelink" value="<?php echo $title; ?>" />
<?php } ?>
</p>
</div>
<?php yourls_do_action( 'shareboxes_middle', $longurl, $shorturl, $title, $text ); ?>
<div id="sharebox" class="share">
<?php echo $share_title; ?>
<div id="tweet">
<span id="charcount" class="hide-if-no-js"><?php echo $count; ?></span>
<textarea id="tweet_body"><?php echo $share; ?></textarea>
</div>
<p id="share_links">Share with
<a id="share_tw" href="http://twitter.com/home?status=<?php echo $_share; ?>" title="Tweet this!" onclick="share('tw');return false">Twitter</a>
<a id="share_fb" href="http://www.facebook.com/share.php?u=<?php echo $_url; ?>" title="Share on Facebook" onclick="share('fb');return false;">Facebook</a>
<a id="share_ff" href="http://friendfeed.com/share/bookmarklet/frame#title=<?php echo $_share; ?>" title="Share on Friendfeed" onclick="javascript:share('ff');return false;">FriendFeed</a>
<?php
yourls_do_action( 'share_links', $longurl, $shorturl, $title, $text );
// Note: on the main admin page, there are no parameters passed to the sharebox when it's drawn.
?>
</p>
</div>
<?php yourls_do_action( 'shareboxes_after', $longurl, $shorturl, $title, $text ); ?>
</div>
<?php
}
// Die die die
function yourls_die( $message = '', $title = '', $header_code = 200 ) {
yourls_status_header( $header_code );
yourls_html_head();
yourls_html_logo();
echo yourls_apply_filter( 'die_title', "<h2>$title</h2>" );
echo yourls_apply_filter( 'die_message', "<p>$message</p>" );
yourls_do_action( 'yourls_die' );
yourls_html_footer();
die();
}
// Add the "Edit" row
function yourls_table_edit_row( $keyword ) {
global $ydb;
$table = YOURLS_DB_TABLE_URL;
$keyword = yourls_sanitize_string( $keyword );
$id = yourls_string2htmlid( $keyword ); // used as HTML #id
$url = yourls_get_keyword_longurl( $keyword );
$title = htmlspecialchars( yourls_get_keyword_title( $keyword ) );
$safe_url = stripslashes( $url );
$safe_title = stripslashes( $title );
$www = yourls_link();
$save_link = yourls_nonce_url( 'save-link_'.$id,
yourls_add_query_arg( array( 'id' => $id, 'action' => 'edit_save', 'keyword' => $keyword ), yourls_admin_url( 'admin-ajax.php' ) )
);
$nonce = yourls_create_nonce( 'edit-save_'.$id );
if( $url ) {
$return = <<<RETURN
<tr id="edit-$id" class="edit-row"><td colspan="5"><strong>Original URL</strong>:<input type="text" id="edit-url-$id" name="edit-url-$id" value="$safe_url" class="text" size="70" /> <strong>Short URL</strong>: $www<input type="text" id="edit-keyword-$id" name="edit-keyword-$id" value="$keyword" class="text" size="10" /><br/><strong>Title</strong>: <input type="text" id="edit-title-$id" name="edit-title-$id" value="$safe_title" class="text" size="60" /></td><td colspan="1"><input type="button" id="edit-submit-$id" name="edit-submit-$id" value="Save" title="Save new values" class="button" onclick="edit_save('$id');" />&nbsp;<input type="button" id="edit-close-$id" name="edit-close-$id" value="X" title="Cancel editing" class="button" onclick="hide_edit('$id');" /><input type="hidden" id="old_keyword_$id" value="$keyword"/><input type="hidden" id="nonce_$id" value="$nonce"/></td></tr>
RETURN;
} else {
$return = '<tr><td colspan="6">Error, URL not found</td></tr>';
}
$return = yourls_apply_filter( 'table_edit_row', $return, $keyword, $url, $title );
return $return;
}
// Add a link row
function yourls_table_add_row( $keyword, $url, $title = '', $ip, $clicks, $timestamp ) {
$keyword = yourls_sanitize_string( $keyword );
$display_keyword = htmlentities( $keyword );
$url = yourls_sanitize_url( $url );
$display_url = htmlentities( yourls_trim_long_string( $url ) );
$title_url = htmlspecialchars( $url );
$title = yourls_sanitize_title( $title ) ;
$display_title = yourls_trim_long_string( $title );
$title = htmlspecialchars( $title );
$id = yourls_string2htmlid( $keyword ); // used as HTML #id
$date = date( 'M d, Y H:i', $timestamp+( YOURLS_HOURS_OFFSET * 3600 ) );
$clicks = number_format( $clicks, 0, '', '' );
$shorturl = yourls_link( $keyword );
$statlink = yourls_statlink( $keyword );
if( yourls_is_ssl() )
$statlink = str_replace( 'http://', 'https://', $statlink );
if( $title ) {
$display_link = "<a href=\"$url\" title=\"$title\">$display_title</a><br/><small><a href=\"$url\" title=\"$title_url\">$display_url</a></small>";
} else {
$display_link = "<a href=\"$url\" title=\"$title_url\">$display_url</a>";
}
$delete_link = yourls_nonce_url( 'delete-link_'.$id,
yourls_add_query_arg( array( 'id' => $id, 'action' => 'delete', 'keyword' => $keyword ), yourls_admin_url( 'admin-ajax.php' ) )
);
$edit_link = yourls_nonce_url( 'edit-link_'.$id,
yourls_add_query_arg( array( 'id' => $id, 'action' => 'edit', 'keyword' => $keyword ), yourls_admin_url( 'admin-ajax.php' ) )
);
// Action button links
$actions = array(
'stats' => array(
'href' => $statlink,
'id' => "statlink-$id",
'title' => 'Stats',
'anchor' => 'Stats',
),
'share' => array(
'href' => '',
'id' => "share-button-$id",
'title' => 'Share',
'anchor' => 'Share',
'onclick' => "toggle_share('$id');return false;",
),
'edit' => array(
'href' => $edit_link,
'id' => "edit-button-$id",
'title' => 'Edit',
'anchor' => 'Edit',
'onclick' => "edit('$id');return false;",
),
'delete' => array(
'href' => $delete_link,
'id' => "delete-button-$id",
'title' => 'Delete',
'anchor' => 'Delete',
'onclick' => "remove('$id');return false;",
)
);
$actions = yourls_apply_filter( 'table_add_row_action_array', $actions );
$action_links = '';
foreach( $actions as $key => $action ) {
$onclick = isset( $action['onclick'] ) ? 'onclick="' . $action['onclick'] . '"' : '' ;
$action_links .= sprintf( '<a href="%s" id="%s" title="%s" class="%s" %s>%s</a>',
$action['href'], $action['id'], $action['title'], 'button button_'.$key, $onclick, $action['anchor']
);
}
$action_links = yourls_apply_filter( 'action_links', $action_links, $keyword, $url, $ip, $clicks, $timestamp );
$row = <<<ROW
<tr id="id-$id"><td id="keyword-$id" class="keyword"><a href="$shorturl">$display_keyword</a></td><td id="url-$id" class="url">$display_link</td><td id="timestamp-$id" class="timestamp">$date</td><td id="ip-$id" class="ip">$ip</td><td id="clicks-$id" class="clicks">$clicks</td><td class="actions" id="actions-$id">$action_links<input type="hidden" id="keyword_$id" value="$keyword"/></td></tr>
ROW;
$row = yourls_apply_filter( 'table_add_row', $row, $keyword, $url, $title, $ip, $clicks, $timestamp );
return $row;
}
// Echo the main table head
function yourls_table_head() {
$start = '<table id="main_table" class="tblSorter" cellpadding="0" cellspacing="1"><thead><tr>'."\n";
echo yourls_apply_filter( 'table_head_start', $start );
$cells = yourls_apply_filter( 'table_head_cells', array(
'shorturl' => 'Short URL&nbsp;',
'longurl' => 'Original URL',
'date' => 'Date',
'ip' => 'IP',
'clicks' => 'Clicks&nbsp;&nbsp;',
'actions' => 'Actions'
) );
foreach( $cells as $k => $v ) {
echo "<th id='main_table_head_$k'>$v</th>\n";
}
$end = "</tr></thead>\n";
echo yourls_apply_filter( 'table_head_end', $end );
}
// Echo the tbody start tag
function yourls_table_tbody_start() {
echo yourls_apply_filter( 'table_tbody_start', '<tbody>' );
}
// Echo the tbody end tag
function yourls_table_tbody_end() {
echo yourls_apply_filter( 'table_tbody_end', '</tbody>' );
}
// Echo the table start tag
function yourls_table_end() {
echo yourls_apply_filter( 'table_end', '</table>' );
}
// Echo HTML tag for a link
function yourls_html_link( $href, $title = '', $element = '' ) {
if( !$title )
$title = $href;
if( $element )
$element = "id='$element'";
echo yourls_apply_filter( 'html_link', "<a href='$href' $element>$title</a>" );
}
// Display the login screen. Nothing past this point.
function yourls_login_screen( $error_msg = '' ) {
yourls_html_head( 'login' );
$action = ( isset($_GET['action']) && $_GET['action'] == 'logout' ? '?' : '' );
yourls_html_logo();
?>
<div id="login">
<form method="post" action="<?php echo $action; ?>"> <?php // reset any QUERY parameters ?>
<?php
if(!empty($error_msg)) {
echo '<p class="error">'.$error_msg.'</p>';
}
?>
<p>
<label for="username">Username</label><br />
<input type="text" id="username" name="username" size="30" class="text" />
</p>
<p>
<label for="password">Password</label><br />
<input type="password" id="password" name="password" size="30" class="text" />
</p>
<p style="text-align: right;">
<input type="submit" id="submit" name="submit" value="Login" class="button" />
</p>
</form>
<script type="text/javascript">$('#username').focus();</script>
</div>
<?php
yourls_html_footer();
die();
}
// Display the admin menu
function yourls_html_menu() {
// Build menu links
if( defined( 'YOURLS_USER' ) ) {
$logout_link = yourls_apply_filter( 'logout_link', 'Hello <strong>' . YOURLS_USER . '</strong> (<a href="?action=logout" title="Logout">Logout</a>)' );
} else {
$logout_link = yourls_apply_filter( 'logout_link', '' );
}
$help_link = yourls_apply_filter( 'help_link', '<a href="' . yourls_site_url( false ) .'/readme.html">Help</a>' );
$admin_links = array();
$admin_sublinks = array();
$admin_links['admin'] = array(
'url' => yourls_admin_url('index.php'),
'title' => 'Go to the admin interface',
'anchor' => 'Admin interface'
);
if( yourls_is_admin() ) {
$admin_links['tools'] = array(
'url' => yourls_admin_url('tools.php'),
'anchor' => 'Tools',
);
$admin_links['plugins'] = array(
'url' => yourls_admin_url('plugins.php'),
'anchor' => 'Manage Plugins',
);
$admin_sublinks['plugins'] = yourls_list_plugin_admin_pages();
}
$admin_links = yourls_apply_filter( 'admin_links', $admin_links );
$admin_sublinks = yourls_apply_filter( 'admin_sublinks', $admin_sublinks );
// Now output menu
echo '<ul id="admin_menu">'."\n";
if ( yourls_is_private() && isset( $logout_link ) )
echo '<li id="admin_menu_logout_link">' . $logout_link .'</li>';
foreach( (array)$admin_links as $link => $ar ) {
if( isset( $ar['url'] ) ) {
$anchor = isset( $ar['anchor'] ) ? $ar['anchor'] : $link;
$title = isset( $ar['title'] ) ? 'title="' . $ar['title'] . '"' : '';
printf( '<li id="admin_menu_%s_link" class="admin_menu_toplevel"><a href="%s" %s>%s</a>', $link, $ar['url'], $title, $anchor );
}
// Output submenu if any. TODO: clean up, too many code duplicated here
if( isset( $admin_sublinks[$link] ) ) {
echo "<ul>\n";
foreach( $admin_sublinks[$link] as $link => $ar ) {
if( isset( $ar['url'] ) ) {
$anchor = isset( $ar['anchor'] ) ? $ar['anchor'] : $link;
$title = isset( $ar['title'] ) ? 'title="' . $ar['title'] . '"' : '';
printf( '<li id="admin_menu_%s_link" class="admin_menu_sublevel admin_menu_sublevel_%s"><a href="%s" %s>%s</a>', $link, $link, $ar['url'], $title, $anchor );
}
}
echo "</ul>\n";
}
}
if ( isset( $help_link ) )
echo '<li id="admin_menu_help_link">' . $help_link .'</li>';
yourls_do_action( 'admin_menu' );
echo "</ul>\n";
yourls_do_action( 'admin_notices' );
yourls_do_action( 'admin_notice' ); // because I never remember if it's 'notices' or 'notice'
/*
To display a notice:
$message = "<div>OMG, dude, I mean!</div>" );
yourls_add_action( 'admin_notices', create_function( '', "echo '$message';" ) );
*/
}
// Wrapper to admin notices
function yourls_add_notice( $message ) {
$message = yourls_notice_box( $message );
yourls_add_action( 'admin_notices', create_function( '', "echo '$message';" ) );
}
// Return a formatted notice
function yourls_notice_box( $message ) {
return <<<HTML
<div class="notice">
<p>$message</p>
</div>
HTML;
}
// Display a page
function yourls_page( $page ) {
$include = YOURLS_ABSPATH . "/pages/$page.php";
if( !file_exists($include) ) {
yourls_die( "Page '$page' not found", 'Not found', 404 );
}
yourls_do_action( 'pre_page', $page );
include($include);
yourls_do_action( 'post_page', $page );
die();
}

171
includes/functions-http.php Normal file
View File

@ -0,0 +1,171 @@
<?php
// TODO: improve this.
// yourls_get_http_transport: use static vars
// yourls_get_remote_content: return array( content, status, code )
// Determine best transport for GET request.
// Order of preference: curl, fopen, fsockopen.
// Return 'curl', 'fopen', 'fsockopen' or false if nothing works
function yourls_get_http_transport( $url ) {
$transports = array();
$scheme = parse_url( $url, PHP_URL_SCHEME );
$is_ssl = ( $scheme == 'https' || $scheme == 'ssl' );
// Test transports by order of preference, best first
// curl
if( function_exists('curl_init') && function_exists('curl_exec') )
$transports[]= 'curl';
// fopen. Doesn't work with https?
if( !$is_ssl && function_exists('fopen') && ini_get('allow_url_fopen') )
$transports[]= 'fopen';
// fsock
if( function_exists( 'fsockopen' ) )
$transports[]= 'fsockopen';
$best = ( $transports ? array_shift( $transports ) : false );
return yourls_apply_filter( 'get_http_transport', $best, $transports );
}
// Get remote content via a GET request using best transport available
// Returns $content (might be an error message) or false if no transport available
function yourls_get_remote_content( $url, $maxlen = 4096, $timeout = 5 ) {
$url = yourls_sanitize_url( $url );
$transport = yourls_get_http_transport( $url );
if( $transport ) {
$content = call_user_func( 'yourls_get_remote_content_'.$transport, $url, $maxlen, $timeout );
} else {
$content = false;
}
return yourls_apply_filter( 'get_remote_content', $content, $url, $maxlen, $timeout );
}
// Get remote content using curl. Needs sanitized $url. Returns $content or false
function yourls_get_remote_content_curl( $url, $maxlen = 4096, $timeout = 5 ) {
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 ); // follow redirects...
curl_setopt( $ch, CURLOPT_MAXREDIRS, 3 ); // ... but not more than 3
curl_setopt( $ch, CURLOPT_USERAGENT, yourls_http_user_agent() );
curl_setopt( $ch, CURLOPT_RANGE, "0-{$maxlen}" ); // Get no more than $maxlen
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 ); // dont check SSL certificates
curl_setopt( $ch, CURLOPT_HEADER, 0 );
$response = curl_exec( $ch );
if( !$response || curl_error( $ch ) ) {
//$response = 'Error: '.curl_error( $ch );
return false;
}
curl_close( $ch );
return substr( $response, 0, $maxlen ); // substr in case CURLOPT_RANGE not supported
}
// Get remote content using fopen. Needs sanitized $url. Returns $content or false
function yourls_get_remote_content_fopen( $url, $maxlen = 4096, $timeout = 5 ) {
$content = false;
$initial_timeout = @ini_set( 'default_socket_timeout', $timeout );
$initial_user_agent = @ini_set( 'user_agent', yourls_http_user_agent() );
// Basic error reporting shortcut
set_error_handler( create_function('$code, $string', 'global $ydb; $ydb->fopen_error = $string;') );
$fp = fopen( $url, 'r');
if( $fp !== false ) {
$buffer = min( $maxlen, 4096 );
while ( !feof( $fp ) && !( strlen( $content ) >= $maxlen ) ) {
$content .= fread( $fp, $buffer );
}
fclose( $fp );
}
if( $initial_timeout !== false )
@ini_set('default_socket_timeout', $initial_timeout);
if( $initial_user_agent !== false )
@ini_set('user_agent', $initial_user_agent);
restore_error_handler();
if( !$content ) {
//global $ydb;
//$content = 'Error: '.strip_tags( $ydb->fopen_error );
return false;
}
return $content;
}
// Get remote content using fsockopen. Needs sanitized $url. Returns $content or false
function yourls_get_remote_content_fsockopen( $url, $maxlen = 4096, $timeout = 5 ) {
// get the host name and url path
$parsed_url = parse_url($url);
$host = $parsed_url['host'];
if ( isset($parsed_url['path']) ) {
$path = $parsed_url['path'];
} else {
$path = '/'; // the url is pointing to the host like http://www.mysite.com
}
if (isset($parsed_url['query'])) {
$path .= '?' . $parsed_url['query'];
}
if (isset($parsed_url['port'])) {
$port = $parsed_url['port'];
} else {
$port = '80';
}
$response = false;
// connect to the remote server
$fp = @fsockopen( $host, $port, $errno, $errstr, $timeout );
var_dump( $errno, $errstr );
if( $fp !== false ) {
// send some fake headers to mimick a standard browser
fputs($fp, "GET $path HTTP/1.0\r\n" .
"Host: $host\r\n" .
"User-Agent: " . yourls_http_user_agent() . "\r\n" .
"Accept: */*\r\n" .
"Accept-Language: en-us,en;q=0.5\r\n" .
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" .
"Keep-Alive: 300\r\n" .
"Connection: keep-alive\r\n" .
"Referer: http://$host\r\n\r\n");
// retrieve the response from the remote server
$buffer = min( $maxlen, 4096 );
while ( !feof( $fp ) && !( strlen( $response ) >= $maxlen ) ) { // get more or less $maxlen bytes (between $maxlen and ($maxlen + ($maxlen-1)) actually)
$response .= fread( $fp, $buffer );
}
fclose( $fp );
} else {
//$response = trim( "Error: #$errno. $errstr" );
return false;
}
// return the file content
return $response;
}
// Return funky user agent string
function yourls_http_user_agent() {
return yourls_apply_filter( 'http_user_agent', 'YOURLS v'.YOURLS_VERSION.' +http://yourls.org/ (running on '.YOURLS_SITE.')' );
}

View File

@ -0,0 +1,282 @@
<?php
// Echoes an image tag of Google Charts map from sorted array of 'country_code' => 'number of visits' (sort by DESC)
function yourls_stats_countries_map( $countries ) {
yourls_do_action( 'stats_countries_map' );
// Echo static map. Will be hidden if JS
$map = array(
'cht' => 't',
'chs' => '440x220',
'chtm'=> 'world',
'chco'=> 'FFFFFF,88C0EB,2A85B3,1F669C',
'chld'=> join('' , array_keys( $countries ) ),
'chd' => 't:'. join(',' , $countries ),
'chf' => 'bg,s,EAF7FE'
);
$map_src = 'http://chart.apis.google.com/chart?' . http_build_query( $map );
//$map_src = yourls_match_current_protocol( $map_src, 'http://chart.apis.', 'https://www.' );
$static = "<img id='yourls_stat_countries_static' class='hide-if-js' src='$map_src' width='440' height='220' border='0' />";
echo yourls_apply_filter( 'stats_countries_static', $static, $countries );
// Echo dynamic map. Will be hidden if no JS
$jsapi = yourls_match_current_protocol( 'http://www.google.com/jsapi' );
$dynamic = <<<MAP
<script type='text/javascript' src='$jsapi'></script>
<script type='text/javascript'>
google.load('visualization', '1', {'packages': ['geomap']});
google.setOnLoadCallback(drawMap);
function drawMap() {
var data = new google.visualization.DataTable();
MAP;
$dynamic .= '
data.addRows('.count( $countries ).');
';
$dynamic .= "
data.addColumn('string', 'Country');
data.addColumn('number', 'Hits');
";
$i = 0;
foreach( $countries as $c => $v ) {
$dynamic .= "
data.setValue($i, 0, '$c');
data.setValue($i, 1, $v);
";
$i++;
}
$dynamic .= <<<MAP
var options = {};
options['dataMode'] = 'regions';
options['width'] = '550px';
options['height'] = '340px';
options['colors'] = [0x88C0EB,0x2A85B3,0x1F669C];
var container = document.getElementById('yourls_stat_countries');
var geomap = new google.visualization.GeoMap(container);
geomap.draw(data, options);
};
</script>
<div id="yourls_stat_countries"></div>
MAP;
echo yourls_apply_filter( 'stats_countries_dynamic', $dynamic, $countries );
}
// Echoes an image tag of Google Charts pie from sorted array of 'data' => 'value' (sort by DESC). Optional $limit = (integer) limit list of X first countries, sorted by most visits
function yourls_stats_pie( $data, $limit = 10, $size = '340x220', $colors = 'C7E7FF,1F669C' ) {
yourls_do_action( 'stats_pie' );
// Trim array: $limit first item + the sum of all others
if ( count( $data ) > $limit ) {
$i= 0;
$trim_data = array('Others' => 0);
foreach( $data as $item=>$value ) {
$i++;
if( $i <= $limit ) {
$trim_data[$item] = $value;
} else {
$trim_data['Others'] += $value;
}
}
$data = $trim_data;
}
// Scale items
$_data = yourls_scale_data( $data );
// Hmmm, pie
$pie = array(
'cht' => 'p',
'chs' => $size,
'chd' => 't:'.( join(',' , $_data ) ),
'chco'=> $colors,
'chl' => join('|' , array_keys( $data ) )
);
$pie_src = 'http://chart.apis.google.com/chart?' . http_build_query( $pie );
//$pie_src = yourls_match_current_protocol( $pie_src, 'http://chart.apis.', 'https://www.' );
list( $size_x, $size_y ) = explode( 'x', $size );
$pie = "<img src='$pie_src' width='$size_x' height='$size_y' border='0' />";
echo yourls_apply_filter( 'stats_pie', $pie, $data, $limit, $size, $colors );
}
// Build a list of all daily values between d1/m1/y1 to d2/m2/y2.
function yourls_build_list_of_days( $dates ) {
/* Say we have an array like:
$dates = array (
2009 => array (
'08' => array (
29 => 15,
30 => 5,
),
'09' => array (
'02' => 3,
'03' => 5,
'04' => 2,
'05' => 99,
),
),
)
*/
if( !$dates )
return array();
// Get first & last years from our range. In our example: 2009 & 2009
$first_year = key( $dates );
$last_year = end( array_keys($dates) );
reset( $dates );
// Get first & last months from our range. In our example: 08 & 09
$first_month = key( $dates[$first_year] );
$last_month = end( array_keys($dates[$last_year]) );
reset( $dates );
// Get first & last days from our range. In our example: 29 & 05
$first_day = key( $dates[$first_year][$first_month] );
$last_day = end( array_keys($dates[$last_year][$last_month]) );
// Now build a list of all years (2009), month (08 & 09) and days (all from 2009-08-29 to 2009-09-05)
$list_of_years = array();
$list_of_months = array();
$list_of_days = array();
for ( $year = $first_year; $year <= $last_year; $year++ ) {
$_year = sprintf('%04d', $year);
$list_of_years[$_year] = $_year;
$current_first_month = ( $year == $first_year ? $first_month : '01' );
$current_last_month = ( $year == $last_year ? $last_month : '12' );
for ( $month = $current_first_month; $month <= $current_last_month; $month++ ) {
$_month = sprintf('%02d', $month);
$list_of_months[$_month] = $_month;
$current_first_day = ( $year == $first_year && $month == $first_month ? $first_day : '01' );
$current_last_day = ( $year == $last_year && $month == $last_month ? $last_day : yourls_days_in_month($month, $year) );
for ( $day = $current_first_day; $day <= $current_last_day; $day++ ) {
$day = sprintf('%02d', $day);
$list_of_days["$_year-$_month-$day"] = isset( $dates[$_year][$_month][$day] ) ? $dates[$_year][$_month][$day] : 0;
}
}
}
return array(
'list_of_days' => $list_of_days,
'list_of_months' => $list_of_months,
'list_of_years' => $list_of_years,
);
}
// Echoes an image tag of Google Charts line graph from array of values (eg 'number of clicks'). $legend1_list & legend2_list are values used for the 2 x-axis labels
function yourls_stats_line( $values, $legend1_list, $legend2_list ) {
yourls_do_action( 'stats_line' );
// If we have only 1 day of data, prepend a fake day with 0 hits for a prettier graph
if ( count( $values ) == 1 )
array_unshift( $values, 0 );
$values = yourls_array_granularity( $values, 30 );
// If x-axis labels have only 1 value, double it for a nicer graph
if( count( $legend1_list ) == 1 )
$legend1_list[] = current( $legend1_list );
if( count( $legend2_list ) == 1 )
$legend2_list[] = current( $legend2_list );
// Make the chart
$legend1 = join('|', $legend1_list );
$legend2 = join('|', $legend2_list );
$max = max( $values );
if ( $max >= 4 ) {
$label_clicks = '0|'.intval( $max / 4 ).'|'.intval( $max / 2 ).'|'.intval( $max / 1.5 ).'|'.$max;
} else {
$label_clicks = array();
for ($i = 0; $i <= $max; $i++) {
$label_clicks[] = $i;
}
$label_clicks = join( '|', $label_clicks );
}
$line = array(
'cht' => 'lc',
'chs' => '440x220',
'chxt'=> 'x,x,y',
'chd' => 't:'.( join(',' , $values ) ),
'chds' => '0,'.$max,
'chm' => 'B,E3F3FF,0,0,0|o,2a85b3,0,-1,6|o,FFFFFF,0,-1,4',
'chco' => '2a85b3',
'chxl'=> '0:|'. $legend1 .'|1:|'. $legend2 .'|2:|'. $label_clicks
);
$line_src = 'http://chart.apis.google.com/chart?' . http_build_query( $line );
//$line_src = yourls_match_current_protocol( $line_src, 'http://chart.apis.', 'https://www.' );
echo yourls_apply_filter( 'stats_line', "<img src='$line_src' />", $values, $legend1_list, $legend2_list );
}
// Return the number of days in a month. From php.net, used if PHP built without calendar functions
function yourls_days_in_month($month, $year) {
// calculate number of days in a month
return $month == 2 ? ($year % 4 ? 28 : ($year % 100 ? 29 : ($year % 400 ? 28 : 29))) : (($month - 1) % 7 % 2 ? 30 : 31);
}
// Get max value from date array of 'year-month-day' = 'hits'
function yourls_stats_get_best_day( $list_of_days ) {
$max = 0; $day = 0;
$max = max( $list_of_days );
foreach( $list_of_days as $k=>$v ) {
if ( $v == $max )
return array( 'day' => $k, 'max' => $max );
}
}
// Return domain of a URL
function yourls_get_domain( $url, $include_scheme = false ) {
$parse = @parse_url( $url ); // Hiding ugly stuff coming from malformed referrer URLs
// Get host & scheme. Fall back to path if not found.
$host = isset( $parse['host'] ) ? $parse['host'] : '';
$scheme = isset( $parse['scheme'] ) ? $parse['scheme'] : '';
$path = isset( $parse['path'] ) ? $parse['path'] : '';
if( !$host )
$host = $path;
if ( $include_scheme && $scheme )
$host = $scheme.'://'.$host;
return $host;
}
// Return favicon URL
function yourls_get_favicon_url( $url ) {
return yourls_match_current_protocol( 'http://www.google.com/s2/u/0/favicons?domain=' . yourls_get_domain( $url, false ) );
}
// Scale array of data from 0 to 100 max
function yourls_scale_data( $data ) {
$max = max( $data );
if( $max > 100 ) {
foreach( $data as $k=>$v ) {
$data[$k] = intval( $v / $max * 100 );
}
}
return $data;
}
// Tweak granularity of array $array: keep only $grain values. This make less accurate but less messy graphs when too much values. See http://code.google.com/apis/chart/formats.html#granularity
function yourls_array_granularity( $array, $grain = 100, $preserve_max = true ) {
if ( count( $array ) > $grain ) {
$max = max( $array );
$step = intval( count( $array ) / $grain );
$i = 0;
// Loop through each item and unset except every $step (optional preserve the max value)
foreach( $array as $k=>$v ) {
$i++;
if ( $i % $step != 0 ) {
if ( $preserve_max == false ) {
unset( $array[$k] );
} else {
if ( $v < $max )
unset( $array[$k] );
}
}
}
}
return $array;
}

View File

@ -0,0 +1,199 @@
<?php
// Check if mod_rewrite is enabled. Note: unused, not reliable enough.
function yourls_check_mod_rewrite() {
return yourls_apache_mod_loaded('mod_rewrite');
}
// Check if extension cURL is enabled
function yourls_check_curl() {
return function_exists('curl_init');
}
// Check if server has MySQL 4.1+
function yourls_check_database_version() {
global $ydb;
return ( version_compare( '4.1', $ydb->mysql_version() ) <= 0 );
}
// Check if PHP > 4.3
function yourls_check_php_version() {
return ( version_compare( '4.3', phpversion() ) <= 0 );
}
// Check if server is an Apache
function yourls_is_apache() {
return (
strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false
|| strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false
);
}
// Check if module exists in Apache config. Input string eg 'mod_rewrite', return true or $default. Stolen from WordPress
function yourls_apache_mod_loaded($mod, $default = false) {
if ( !yourls_is_apache() )
return false;
if ( function_exists('apache_get_modules') ) {
$mods = apache_get_modules();
if ( in_array($mod, $mods) )
return true;
} elseif ( function_exists('phpinfo') ) {
ob_start();
phpinfo(8);
$phpinfo = ob_get_clean();
if ( false !== strpos($phpinfo, $mod) )
return true;
}
return $default;
}
// Create .htaccess. Returns boolean
function yourls_create_htaccess() {
$host = parse_url( YOURLS_SITE );
$path = ( isset( $host['path'] ) ? $host['path'] : '' );
$content = array(
'<IfModule mod_rewrite.c>',
'RewriteEngine On',
'RewriteBase '.$path.'/',
'RewriteCond %{REQUEST_FILENAME} !-f',
'RewriteCond %{REQUEST_FILENAME} !-d',
'RewriteRule ^(.*)$ '.$path.'/yourls-loader.php [L]',
'</IfModule>',
);
$filename = YOURLS_ABSPATH.'/.htaccess';
return ( yourls_insert_with_markers( $filename, 'YOURLS', $content ) );
}
// Inserts $insertion (text in an array of lines) into $filename (.htaccess) between BEGIN/END $marker block. Returns bool. Stolen from WP
function yourls_insert_with_markers( $filename, $marker, $insertion ) {
if (!file_exists( $filename ) || is_writeable( $filename ) ) {
if (!file_exists( $filename ) ) {
$markerdata = '';
} else {
$markerdata = explode( "\n", implode( '', file( $filename ) ) );
}
if ( !$f = @fopen( $filename, 'w' ) )
return false;
$foundit = false;
if ( $markerdata ) {
$state = true;
foreach ( $markerdata as $n => $markerline ) {
if (strpos($markerline, '# BEGIN ' . $marker) !== false)
$state = false;
if ( $state ) {
if ( $n + 1 < count( $markerdata ) )
fwrite( $f, "{$markerline}\n" );
else
fwrite( $f, "{$markerline}" );
}
if (strpos($markerline, '# END ' . $marker) !== false) {
fwrite( $f, "# BEGIN {$marker}\n" );
if ( is_array( $insertion ))
foreach ( $insertion as $insertline )
fwrite( $f, "{$insertline}\n" );
fwrite( $f, "# END {$marker}\n" );
$state = true;
$foundit = true;
}
}
}
if (!$foundit) {
fwrite( $f, "\n\n# BEGIN {$marker}\n" );
foreach ( $insertion as $insertline )
fwrite( $f, "{$insertline}\n" );
fwrite( $f, "# END {$marker}\n\n" );
}
fclose( $f );
return true;
} else {
return false;
}
}
// Create MySQL tables. Return array( 'success' => array of success strings, 'errors' => array of error strings )
function yourls_create_sql_tables() {
global $ydb;
$error_msg = array();
$success_msg = array();
// Create Table Query
$create_tables = array();
$create_tables[YOURLS_DB_TABLE_URL] =
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_URL.'` ('.
'`keyword` varchar(200) BINARY NOT NULL,'.
'`url` text BINARY NOT NULL,'.
'`title` text CHARACTER SET utf8,'.
'`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,'.
'`ip` VARCHAR(41) NOT NULL,'.
'`clicks` INT(10) UNSIGNED NOT NULL,'.
' PRIMARY KEY (`keyword`),'.
' KEY `timestamp` (`timestamp`),'.
' KEY `ip` (`ip`)'.
');';
$create_tables[YOURLS_DB_TABLE_OPTIONS] =
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_OPTIONS.'` ('.
'`option_id` bigint(20) unsigned NOT NULL auto_increment,'.
'`option_name` varchar(64) NOT NULL default "",'.
'`option_value` longtext NOT NULL,'.
'PRIMARY KEY (`option_id`,`option_name`),'.
'KEY `option_name` (`option_name`)'.
') AUTO_INCREMENT=1 ;';
$create_tables[YOURLS_DB_TABLE_LOG] =
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_LOG.'` ('.
'`click_id` int(11) NOT NULL auto_increment,'.
'`click_time` datetime NOT NULL,'.
'`shorturl` varchar(200) BINARY NOT NULL,'.
'`referrer` varchar(200) NOT NULL,'.
'`user_agent` varchar(255) NOT NULL,'.
'`ip_address` varchar(41) NOT NULL,'.
'`country_code` char(2) NOT NULL,'.
'PRIMARY KEY (`click_id`),'.
'KEY `shorturl` (`shorturl`)'.
') AUTO_INCREMENT=1 ;';
$create_table_count = 0;
$ydb->show_errors = true;
// Create tables
foreach ( $create_tables as $table_name => $table_query ) {
$ydb->query($table_query);
$create_success = $ydb->query("SHOW TABLES LIKE '$table_name'");
if($create_success) {
$create_table_count++;
$success_msg[] = "Table '$table_name' created.";
} else {
$error_msg[] = "Error creating table '$table_name'.";
}
}
// Insert data into tables
yourls_update_option( 'version', YOURLS_VERSION );
yourls_update_option( 'db_version', YOURLS_DB_VERSION );
yourls_update_option( 'next_id', 1 );
// Insert sample links
yourls_insert_link_in_db( 'http://planetozh.com/blog/', 'ozhblog', 'planetOzh: Ozh\' blog' );
yourls_insert_link_in_db( 'http://ozh.org/', 'ozh', 'ozh.org' );
yourls_insert_link_in_db( 'http://yourls.org/', 'yourls', 'YOURLS: Your Own URL Shortener' );
// Check results of operations
if ( sizeof($create_tables) == $create_table_count ) {
$success_msg[] = 'YOURLS tables successfully created.';
} else {
$error_msg[] = "Error creating YOURLS tables.";
}
return array( 'success' => $success_msg, 'error' => $error_msg );
}
?>

View File

@ -0,0 +1,508 @@
<?php
/**
* The filter/plugin API is located in this file, which allows for creating filters
* and hooking functions, and methods. The functions or methods will be run when
* the filter is called.
*
* Any of the syntaxes explained in the PHP documentation for the
* {@link http://us2.php.net/manual/en/language.pseudo-types.php#language.types.callback 'callback'}
* type are valid.
*
* This API is heavily inspired by the one I implemented in Zenphoto 1.3, which was heavily inspired by the one used in WordPress.
*
* @author Ozh
* @since 1.5
*/
$yourls_filters = array();
/* This global var will collect filters with the following structure:
* $yourls_filters['hook']['array of priorities']['serialized function names']['array of ['array (functions, accepted_args)]']
*/
/**
* Registers a filtering function
*
* Typical use:
* yourls_add_filter('some_hook', 'function_handler_for_hook');
*
* @global array $yourls_filters Storage for all of the filters
* @param string $hook the name of the YOURLS element to be filtered or YOURLS action to be triggered
* @param callback $function_name the name of the function that is to be called.
* @param integer $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default=10, lower=earlier execution, and functions with the same priority are executed in the order in which they were added to the filter)
* @param int $accepted_args optional. The number of arguments the function accept (default is the number provided).
*/
function yourls_add_filter( $hook, $function_name, $priority = 10, $accepted_args = NULL, $type = 'filter' ) {
global $yourls_filters;
// At this point, we cannot check if the function exists, as it may well be defined later (which is OK)
$id = yourls_filter_unique_id( $hook, $function_name, $priority );
$yourls_filters[$hook][$priority][$id] = array(
'function' => $function_name,
'accepted_args' => $accepted_args,
'type' => $type,
);
}
/**
* Hooks a function on to a specific action.
*
* Actions are the hooks that YOURLS launches at specific points
* during execution, or when specific events occur. Plugins can specify that
* one or more of its PHP functions are executed at these points, using the
* Action API.
*
* @param string $hook The name of the action to which the $function_to_add is hooked.
* @param callback $function_name The name of the function you wish to be called.
* @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
* @param int $accepted_args optional. The number of arguments the function accept (default 1).
*/
function yourls_add_action( $hook, $function_name, $priority = 10, $accepted_args = 1 ) {
return yourls_add_filter( $hook, $function_name, $priority, $accepted_args, 'action' );
}
/**
* Build Unique ID for storage and retrieval.
*
* Simply using a function name is not enough, as several functions can have the same name when they are enclosed in classes.
*
* @global array $yourls_filters storage for all of the filters
* @param string $hook hook to which the function is attached
* @param string|array $function used for creating unique id
* @param int|bool $priority used in counting how many hooks were applied. If === false and $function is an object reference, we return the unique id only if it already has one, false otherwise.
* @param string $type filter or action
* @return string unique ID for usage as array key
*/
function yourls_filter_unique_id( $hook, $function, $priority ) {
global $yourls_filters;
// If function then just skip all of the tests and not overwrite the following.
if ( is_string($function) )
return $function;
// Object Class Calling
else if (is_object($function[0]) ) {
$obj_idx = get_class($function[0]).$function[1];
if ( !isset($function[0]->_yourls_filters_id) ) {
if ( false === $priority )
return false;
$count = isset($yourls_filters[$hook][$priority]) ? count((array)$yourls_filters[$hook][$priority]) : 0;
$function[0]->_yourls_filters_id = $count;
$obj_idx .= $count;
unset($count);
} else
$obj_idx .= $function[0]->_yourls_filters_id;
return $obj_idx;
}
// Static Calling
else if ( is_string($function[0]) )
return $function[0].$function[1];
}
/**
* Performs a filtering operation on a YOURLS element or event.
*
* Typical use:
*
* 1) Modify a variable if a function is attached to hook 'yourls_hook'
* $yourls_var = "default value";
* $yourls_var = yourls_apply_filter( 'yourls_hook', $yourls_var );
*
* 2) Trigger functions is attached to event 'yourls_event'
* yourls_apply_filter( 'yourls_event' );
* (see yourls_do_action() )
*
* Returns an element which may have been filtered by a filter.
*
* @global array $yourls_filters storage for all of the filters
* @param string $hook the name of the YOURLS element or action
* @param mixed $value the value of the element before filtering
* @return mixed
*/
function yourls_apply_filter( $hook, $value = '' ) {
global $yourls_filters;
if ( !isset( $yourls_filters[$hook] ) )
return $value;
$args = func_get_args();
// Sort filters by priority
ksort( $yourls_filters[$hook] );
// Loops through each filter
reset( $yourls_filters[$hook] );
do {
foreach( (array) current($yourls_filters[$hook]) as $the_ ) {
if ( !is_null($the_['function']) ){
$args[1] = $value;
$count = $the_['accepted_args'];
if (is_null($count)) {
$_value = call_user_func_array($the_['function'], array_slice($args, 1));
} else {
$_value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $count));
}
}
if( $the_['type'] == 'filter' )
$value = $_value;
}
} while ( next($yourls_filters[$hook]) !== false );
if( $the_['type'] == 'filter' )
return $value;
}
function yourls_do_action( $hook, $arg = '' ) {
$args = array();
if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) ) // array(&$this)
$args[] =& $arg[0];
else
$args[] = $arg;
for ( $a = 2; $a < func_num_args(); $a++ )
$args[] = func_get_arg($a);
yourls_apply_filter( $hook, $args );
}
/**
* Removes a function from a specified filter hook.
*
* This function removes a function attached to a specified filter hook. This
* method can be used to remove default functions attached to a specific filter
* hook and possibly replace them with a substitute.
*
* To remove a hook, the $function_to_remove and $priority arguments must match
* when the hook was added.
*
* @global array $yourls_filters storage for all of the filters
* @param string $hook The filter hook to which the function to be removed is hooked.
* @param callback $function_to_remove The name of the function which should be removed.
* @param int $priority optional. The priority of the function (default: 10).
* @param int $accepted_args optional. The number of arguments the function accepts (default: 1).
* @return boolean Whether the function was registered as a filter before it was removed.
*/
function yourls_remove_filter( $hook, $function_to_remove, $priority = 10, $accepted_args = 1 ) {
global $yourls_filters;
$function_to_remove = yourls_filter_unique_id($hook, $function_to_remove, $priority);
$remove = isset ($yourls_filters[$hook][$priority][$function_to_remove]);
if ( $remove === true ) {
unset ($yourls_filters[$hook][$priority][$function_to_remove]);
if ( empty($yourls_filters[$hook][$priority]) )
unset ($yourls_filters[$hook]);
}
return $remove;
}
/**
* Check if any filter has been registered for a hook.
*
* @global array $yourls_filters storage for all of the filters
* @param string $hook The name of the filter hook.
* @param callback $function_to_check optional. If specified, return the priority of that function on this hook or false if not attached.
* @return int|boolean Optionally returns the priority on that hook for the specified function.
*/
function yourls_has_filter( $hook, $function_to_check = false ) {
global $yourls_filters;
$has = !empty($yourls_filters[$hook]);
if ( false === $function_to_check || false == $has ) {
return $has;
}
if ( !$idx = yourls_filter_unique_id($hook, $function_to_check, false) ) {
return false;
}
foreach ( (array) array_keys($yourls_filters[$hook]) as $priority ) {
if ( isset($yourls_filters[$hook][$priority][$idx]) )
return $priority;
}
return false;
}
function yourls_has_action( $hook, $function_to_check = false ) {
return yourls_has_filter( $hook, $function_to_check );
}
/**
* Return number of active plugins
*
* @return integer Number of activated plugins
*/
function yourls_has_active_plugins( ) {
global $ydb;
if( !property_exists( $ydb, 'plugins' ) || !$ydb->plugins )
$ydb->plugins = array();
return count( $ydb->plugins );
}
/**
* List plugins in /user/plugins
*
* @global $ydb Storage of mostly everything YOURLS needs to know
* @return array Array of [/plugindir/plugin.php]=>array('Name'=>'Ozh', 'Title'=>'Hello', )
*/
function yourls_get_plugins( ) {
global $ydb;
$plugins = (array) glob( YOURLS_PLUGINDIR .'/*/plugin.php');
if( !$plugins )
return array();
foreach( $plugins as $key=>$plugin ) {
$_plugin = yourls_plugin_basename( $plugin );
$plugins[ $_plugin ] = yourls_get_plugin_data( $plugin );
unset( $plugins[ $key ] );
}
return $plugins;
}
/**
* Check if a plugin is active
*
* @param string $file Physical path to plugin file
* @return bool
*/
function yourls_is_active_plugin( $plugin ) {
if( !yourls_has_active_plugins( ) )
return false;
global $ydb;
$plugin = yourls_plugin_basename( $plugin );
return in_array( $plugin, $ydb->plugins );
}
/**
* Parse a plugin header
*
* @param string $file Physical path to plugin file
* @return array Array of 'Field'=>'Value' from plugin comment header lines of the form "Field: Value"
*/
function yourls_get_plugin_data( $file ) {
$fp = fopen( $file, 'r' ); // assuming $file is readable, since yourls_load_plugins() filters this
$data = fread( $fp, 8192 ); // get first 8kb
fclose( $fp );
// Capture all the header within first comment block
if( !preg_match( '!.*?/\*(.*?)\*/!ms', $data, $matches ) )
return array();
// Capture each line with "Something: some text"
unset( $data );
$lines = preg_split( "[\n|\r]", $matches[1] );
unset( $matches );
$plugin_data = array();
foreach( $lines as $line ) {
if( !preg_match( '!(.*?):\s+(.*)!', $line, $matches ) )
continue;
list( $null, $field, $value ) = array_map( 'trim', $matches);
$plugin_data[ $field ] = $value;
}
return $plugin_data;
}
// Include active plugins
function yourls_load_plugins() {
global $ydb;
$ydb->plugins = array();
$active_plugins = yourls_get_option( 'active_plugins' );
// Don't load plugins when installing or updating
if( !$active_plugins OR ( defined( 'YOURLS_INSTALLING' ) AND YOURLS_INSTALLING ) OR yourls_upgrade_is_needed() )
return;
foreach( (array)$active_plugins as $key=>$plugin ) {
if( yourls_validate_plugin_file( YOURLS_PLUGINDIR.'/'.$plugin ) ) {
include_once( YOURLS_PLUGINDIR.'/'.$plugin );
$ydb->plugins[] = $plugin;
unset( $active_plugins[$key] );
}
}
// $active_plugins should be empty now, if not, a plugin could not be find: remove it
if( count( $active_plugins ) ) {
$missing = '<strong>'.join( '</strong>, <strong>', $active_plugins ).'</strong>';
yourls_update_option( 'active_plugins', $ydb->plugins );
$message = 'Could not find and deactivated '. yourls_plural( 'plugin', count( $active_plugins ) ) .' '. $missing;
yourls_add_notice( $message );
}
}
/**
* Check if a file is safe for inclusion (well, "safe", no guarantee)
*
* @param string $file Full pathname to a file
*/
function yourls_validate_plugin_file( $file ) {
if (
false !== strpos( $file, '..' )
OR
false !== strpos( $file, './' )
OR
'plugin.php' !== substr( $file, -10 ) // a plugin must be named 'plugin.php'
OR
!is_readable( $file )
)
return false;
return true;
}
/**
* Activate a plugin
*
* @param string $plugin Plugin filename (full or relative to plugins directory)
* @return mixed string if error or true if success
*/
function yourls_activate_plugin( $plugin ) {
// validate file
$plugin = yourls_plugin_basename( $plugin );
$plugindir = yourls_sanitize_filename( YOURLS_PLUGINDIR );
if( !yourls_validate_plugin_file( $plugindir.'/'.$plugin ) )
return 'Not a valid plugin file';
// check not activated already
global $ydb;
if( yourls_has_active_plugins() && in_array( $plugin, $ydb->plugins ) )
return 'Plugin already activated';
// attempt activation. TODO: uber cool fail proof sandbox like in WP.
ob_start();
include( YOURLS_PLUGINDIR.'/'.$plugin );
if ( ob_get_length() > 0 ) {
// there was some output: error
$output = ob_get_clean();
return 'Plugin generated expected output. Error was: <br/><pre>'.$output.'</pre>';
}
// so far, so good: update active plugin list
$ydb->plugins[] = $plugin;
yourls_update_option( 'active_plugins', $ydb->plugins );
yourls_do_action( 'activated_plugin', $plugin );
yourls_do_action( 'activated_' . $plugin );
return true;
}
/**
* Dectivate a plugin
*
* @param string $plugin Plugin filename (full relative to plugins directory)
* @return mixed string if error or true if success
*/
function yourls_deactivate_plugin( $plugin ) {
$plugin = yourls_plugin_basename( $plugin );
// Check plugin is active
if( !yourls_is_active_plugin( $plugin ) )
return 'Plugin not active';
// Deactivate the plugin
global $ydb;
$key = array_search( $plugin, $ydb->plugins );
if( $key !== false ) {
array_splice( $ydb->plugins, $key, 1 );
}
yourls_update_option( 'active_plugins', $ydb->plugins );
yourls_do_action( 'deactivated_plugin', $plugin );
yourls_do_action( 'deactivated_' . $plugin );
return true;
}
/**
* Return the path of a plugin file, relative to the plugins directory
*/
function yourls_plugin_basename( $file ) {
$file = yourls_sanitize_filename( $file );
$plugindir = yourls_sanitize_filename( YOURLS_PLUGINDIR );
$file = str_replace( $plugindir, '', $file );
return trim( $file, '/' );
}
/**
* Return the URL of the directory a plugin
*/
function yourls_plugin_url( $file ) {
$url = YOURLS_PLUGINURL . '/' . yourls_plugin_basename( $file );
if( yourls_is_ssl() or yourls_needs_ssl() )
$url = str_replace('http://', 'https://', $url);
return yourls_apply_filter( 'plugin_url', $url, $file );
}
/**
* Display list of links to plugin admin pages, if any
*/
function yourls_list_plugin_admin_pages() {
global $ydb;
if( !property_exists( $ydb, 'plugin_pages' ) || !$ydb->plugin_pages )
return;
$plugin_links = array();
foreach( (array)$ydb->plugin_pages as $plugin => $page ) {
$plugin_links[$plugin] = array(
'url' => yourls_admin_url( 'plugins.php?page='.$page['slug'] ),
'anchor' => $page['title'],
);
}
return $plugin_links;
}
/**
* Register a plugin administration page
*/
function yourls_register_plugin_page( $slug, $title, $function ) {
global $ydb;
if( !property_exists( $ydb, 'plugin_pages' ) || !$ydb->plugin_pages )
$ydb->plugin_pages = array();
$ydb->plugin_pages[ $slug ] = array(
'slug' => $slug,
'title' => $title,
'function' => $function,
);
}
/**
* Handle plugin administration page
*
*/
function yourls_plugin_admin_page( $plugin_page ) {
global $ydb;
// Check the plugin page is actually registered
if( !isset( $ydb->plugin_pages[$plugin_page] ) ) {
yourls_die( 'This page does not exist. Maybe a plugin you thought was activated is inactive?', 'Invalid link' );
}
// Draw the page itself
yourls_do_action( 'load-' . $plugin_page);
yourls_html_head( 'plugin_page_' . $plugin_page, $ydb->plugin_pages[$plugin_page]['title'] );
yourls_html_logo();
yourls_html_menu();
call_user_func( $ydb->plugin_pages[$plugin_page]['function'] );
yourls_html_footer();
die();
}

View File

@ -0,0 +1,309 @@
<?php
// Upgrade YOURLS and DB schema
function yourls_upgrade( $step, $oldver, $newver, $oldsql, $newsql ) {
// special case for 1.3: the upgrade is a multi step procedure
if( $oldsql == 100 ) {
yourls_upgrade_to_14( $step );
}
// other upgrades which are done in a single pass
switch( $step ) {
case 1:
case 2:
if( $oldsql < 210 )
yourls_upgrade_to_141();
if( $oldsql < 220 )
yourls_upgrade_to_143();
if( $oldsql < 250 )
yourls_upgrade_to_15();
if( $oldsql < 482 )
yourls_upgrade_482();
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=3" ) );
break;
case 3:
// Update options to reflect latest version
yourls_update_option( 'version', YOURLS_VERSION );
yourls_update_option( 'db_version', YOURLS_DB_VERSION );
break;
}
}
// Upgrade r482
function yourls_upgrade_482() {
// Change URL title charset to UTF8
global $ydb;
$table_url = YOURLS_DB_TABLE_URL;
$sql = "ALTER TABLE `$table_url` CHANGE `title` `title` TEXT CHARACTER SET utf8;";
$ydb->query( $sql );
echo "<p>Updating table structure. Please wait...</p>";
}
/************************** 1.4.3 -> 1.5 **************************/
// Main func for upgrade from 1.4.3 to 1.5
function yourls_upgrade_to_15( ) {
// Create empty 'active_plugins' entry in the option if needed
if( yourls_get_option( 'active_plugins' ) === false )
yourls_add_option( 'active_plugins', array() );
echo "<p>Enabling the plugin API. Please wait...</p>";
// Alter URL table to store titles
global $ydb;
$table_url = YOURLS_DB_TABLE_URL;
$sql = "ALTER TABLE `$table_url` ADD `title` TEXT AFTER `url`;";
$ydb->query( $sql );
echo "<p>Updating table structure. Please wait...</p>";
// Update .htaccess
yourls_create_htaccess();
echo "<p>Updating .htaccess file. Please wait...</p>";
}
/************************** 1.4.1 -> 1.4.3 **************************/
// Main func for upgrade from 1.4.1 to 1.4.3
function yourls_upgrade_to_143( ) {
// Check if we have 'keyword' (borked install) or 'shorturl' (ok install)
global $ydb;
$table_log = YOURLS_DB_TABLE_LOG;
$sql = "SHOW COLUMNS FROM `$table_log`";
$cols = $ydb->get_results( $sql );
if ( $cols[2]->Field == 'keyword' ) {
$sql = "ALTER TABLE `$table_log` CHANGE `keyword` `shorturl` VARCHAR( 200 ) BINARY;";
$ydb->query( $sql );
}
echo "<p>Structure of existing tables updated. Please wait...</p>";
}
/************************** 1.4 -> 1.4.1 **************************/
// Main func for upgrade from 1.4 to 1.4.1
function yourls_upgrade_to_141( ) {
// Kill old cookies from 1.3 and prior
setcookie('yourls_username', null, time() - 3600 );
setcookie('yourls_password', null, time() - 3600 );
// alter table URL
yourls_alter_url_table_to_141();
// recreate the htaccess file if needed
yourls_create_htaccess();
}
// Alter table URL to 1.4.1
function yourls_alter_url_table_to_141() {
global $ydb;
$table_url = YOURLS_DB_TABLE_URL;
$alter = "ALTER TABLE `$table_url` CHANGE `keyword` `keyword` VARCHAR( 200 ) BINARY, CHANGE `url` `url` TEXT BINARY ";
$ydb->query( $alter );
echo "<p>Structure of existing tables updated. Please wait...</p>";
}
/************************** 1.3 -> 1.4 **************************/
// Main func for upgrade from 1.3-RC1 to 1.4
function yourls_upgrade_to_14( $step ) {
switch( $step ) {
case 1:
// create table log & table options
// update table url structure
// update .htaccess
yourls_create_tables_for_14(); // no value returned, assuming it went OK
yourls_alter_url_table_to_14(); // no value returned, assuming it went OK
$clean = yourls_clean_htaccess_for_14(); // returns bool
$create = yourls_create_htaccess(); // returns bool
if ( !$create )
echo "<p class='warning'>Please create your <tt>.htaccess</tt> file (I could not do it for you). Please refer to <a href='http://yourls.org/htaccess'>http://yourls.org/htaccess</a>.";
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=2&oldver=1.3&newver=1.4&oldsql=100&newsql=200" ), $create );
break;
case 2:
// convert each link in table url
yourls_update_table_to_14();
break;
case 3:
// update table url structure part 2: recreate indexes
yourls_alter_url_table_to_14_part_two();
// update version & db_version & next_id in the option table
// attempt to drop YOURLS_DB_TABLE_NEXTDEC
yourls_update_options_to_14();
// Now upgrade to 1.4.1
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=1&oldver=1.4&newver=1.4.1&oldsql=200&newsql=210" ) );
break;
}
}
// Update options to reflect new version
function yourls_update_options_to_14() {
yourls_update_option( 'version', '1.4' );
yourls_update_option( 'db_version', '200' );
if( defined('YOURLS_DB_TABLE_NEXTDEC') ) {
global $ydb;
$table = YOURLS_DB_TABLE_NEXTDEC;
$next_id = $ydb->get_var("SELECT `next_id` FROM `$table`");
yourls_update_option( 'next_id', $next_id );
@$ydb->query( "DROP TABLE `$table`" );
} else {
yourls_update_option( 'next_id', 1 ); // In case someone mistakenly deleted the next_id constant or table too early
}
}
// Create new tables for YOURLS 1.4: options & log
function yourls_create_tables_for_14() {
global $ydb;
$queries = array();
$queries[YOURLS_DB_TABLE_OPTIONS] =
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_OPTIONS.'` ('.
'`option_id` int(11) unsigned NOT NULL auto_increment,'.
'`option_name` varchar(64) NOT NULL default "",'.
'`option_value` longtext NOT NULL,'.
'PRIMARY KEY (`option_id`,`option_name`),'.
'KEY `option_name` (`option_name`)'.
');';
$queries[YOURLS_DB_TABLE_LOG] =
'CREATE TABLE IF NOT EXISTS `'.YOURLS_DB_TABLE_LOG.'` ('.
'`click_id` int(11) NOT NULL auto_increment,'.
'`click_time` datetime NOT NULL,'.
'`shorturl` varchar(200) NOT NULL,'.
'`referrer` varchar(200) NOT NULL,'.
'`user_agent` varchar(255) NOT NULL,'.
'`ip_address` varchar(41) NOT NULL,'.
'`country_code` char(2) NOT NULL,'.
'PRIMARY KEY (`click_id`),'.
'KEY `shorturl` (`shorturl`)'.
');';
foreach( $queries as $query ) {
$ydb->query( $query ); // There's no result to be returned to check if table was created (except making another query to check table existence, which we'll avoid)
}
echo "<p>New tables created. Please wait...</p>";
}
// Alter table structure, part 1 (change schema, drop index)
function yourls_alter_url_table_to_14() {
global $ydb;
$table = YOURLS_DB_TABLE_URL;
$alters = array();
$results = array();
$alters[] = "ALTER TABLE `$table` CHANGE `id` `keyword` VARCHAR( 200 ) NOT NULL";
$alters[] = "ALTER TABLE `$table` CHANGE `url` `url` TEXT NOT NULL";
$alters[] = "ALTER TABLE `$table` DROP PRIMARY KEY";
foreach ( $alters as $query ) {
$ydb->query( $query );
}
echo "<p>Structure of existing tables updated. Please wait...</p>";
}
// Alter table structure, part 2 (recreate indexes after the table is up to date)
function yourls_alter_url_table_to_14_part_two() {
global $ydb;
$table = YOURLS_DB_TABLE_URL;
$alters = array();
$alters[] = "ALTER TABLE `$table` ADD PRIMARY KEY ( `keyword` )";
$alters[] = "ALTER TABLE `$table` ADD INDEX ( `ip` )";
$alters[] = "ALTER TABLE `$table` ADD INDEX ( `timestamp` )";
foreach ( $alters as $query ) {
$ydb->query( $query );
}
echo "<p>New table index created</p>";
}
// Convert each link from 1.3 (id) to 1.4 (keyword) structure
function yourls_update_table_to_14() {
global $ydb;
$table = YOURLS_DB_TABLE_URL;
// Modify each link to reflect new structure
$chunk = 45;
$from = isset($_GET['from']) ? intval( $_GET['from'] ) : 0 ;
$total = yourls_get_db_stats();
$total = $total['total_links'];
$sql = "SELECT `keyword`,`url` FROM `$table` WHERE 1=1 ORDER BY `url` ASC LIMIT $from, $chunk ;";
$rows = $ydb->get_results($sql);
$count = 0;
$queries = 0;
foreach( $rows as $row ) {
$keyword = $row->keyword;
$url = $row->url;
$newkeyword = yourls_int2string( $keyword );
$ydb->query("UPDATE `$table` SET `keyword` = '$newkeyword' WHERE `url` = '$url';");
if( $ydb->result === true ) {
$queries++;
} else {
echo "<p>Huho... Could not update rown with url='$url', from keyword '$keyword' to keyword '$newkeyword'</p>"; // Find what went wrong :/
}
$count++;
}
// All done for this chunk of queries, did it all go as expected?
$success = true;
if( $count != $queries ) {
$success = false;
$num = $count - $queries;
echo "<p>$num error(s) occured while updating the URL table :(</p>";
}
if ( $count == $chunk ) {
// there are probably other rows to convert
$from = $from + $chunk;
$remain = $total - $from;
echo "<p>Converted $chunk database rows ($remain remaining). Continuing... Please do not close this window until it's finished!</p>";
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=2&oldver=1.3&newver=1.4&oldsql=100&newsql=200&from=$from" ), $success );
} else {
// All done
echo '<p>All rows converted! Please wait...</p>';
yourls_redirect_javascript( yourls_admin_url( "upgrade.php?step=3&oldver=1.3&newver=1.4&oldsql=100&newsql=200" ), $success );
}
}
// Clean .htaccess as it existed before 1.4. Returns boolean
function yourls_clean_htaccess_for_14() {
$filename = YOURLS_ABSPATH.'/.htaccess';
$result = false;
if( is_writeable( $filename ) ) {
$contents = implode( '', file( $filename ) );
// remove "ShortURL" block
$contents = preg_replace( '/# BEGIN ShortURL.*# END ShortURL/s', '', $contents );
// comment out deprecated RewriteRule
$find = 'RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]';
$replace = "# You can safely remove this 5 lines block -- it's no longer used in YOURLS\n".
"# $find";
$contents = str_replace( $find, $replace, $contents );
// Write cleaned file
$f = fopen( $filename, 'w' );
fwrite( $f, $contents );
fclose( $f );
$result = true;
}
return $result;
}

View File

@ -0,0 +1,83 @@
<?php
/*************************************************************************/
/* This class stores associative arrays in an xml formated string. */
/* There's also a function thar retrieves them. If you try to use */
/* xml2array with a general xml, it can fail, since there can be some */
/* repeated indexes.... */
/* Source: http://www.phpclasses.org/browse/package/2286/ */
/*************************************************************************/
class yourls_array2xml {
var $text;
var $arrays, $keys, $node_flag, $depth, $xml_parser;
/*Converts an array to an xml string*/
function array2xml($array) {
//global $text;
$this->text="<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><result>";
$this->text.= $this->array_transform($array);
$this->text .="</result>";
return $this->text;
}
function array_transform($array){
//global $array_text;
foreach($array as $key => $value){
if(!is_array($value)){
//BEGIN code mod by Doug Vanderweide, 13 Jan 2011
//does $value contain html entities?
if(strlen($value) != strlen(htmlentities($value))) {
//if so, encode as CDATA
$value = "<![CDATA[" . htmlentities($value) . "]]>";
}
$this->text .= "<$key>$value</$key>";
//END code mod
} else {
$this->text.="<$key>";
$this->array_transform($value);
$this->text.="</$key>";
}
}
//return $array_text;
}
/*Transform an XML string to associative array "XML Parser Functions"*/
function xml2array($xml){
$this->depth=-1;
$this->xml_parser = xml_parser_create();
xml_set_object($this->xml_parser, $this);
xml_parser_set_option ($this->xml_parser,XML_OPTION_CASE_FOLDING,0);//Don't put tags uppercase
xml_set_element_handler($this->xml_parser, "startElement", "endElement");
xml_set_character_data_handler($this->xml_parser,"characterData");
xml_parse($this->xml_parser,$xml,true);
xml_parser_free($this->xml_parser);
return $this->arrays[0];
}
function startElement($parser, $name, $attrs)
{
$this->keys[]=$name; //We add a key
$this->node_flag=1;
$this->depth++;
}
function characterData($parser,$data)
{
$key=end($this->keys);
$this->arrays[$this->depth][$key]=$data;
$this->node_flag=0; //So that we don't add as an array, but as an element
}
function endElement($parser, $name)
{
$key=array_pop($this->keys);
//If $node_flag==1 we add as an array, if not, as an element
if($this->node_flag==1){
$this->arrays[$this->depth][$key]=$this->arrays[$this->depth+1];
unset($this->arrays[$this->depth+1]);
}
$this->node_flag=1;
$this->depth--;
}
}//End of the class
?>

1673
includes/functions.php Normal file

File diff suppressed because it is too large Load Diff

BIN
includes/geo/GeoIP.dat Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Some files were not shown because too many files have changed in this diff Show More