Merge branch 'master' of github.com:shish/shimmie2

This commit is contained in:
Shish 2012-03-31 12:38:44 +01:00
commit 567755a185
49 changed files with 348 additions and 491 deletions

5
.gitignore vendored
View File

@ -1,12 +1,7 @@
.svn
backup
config.php
data
images
imgdump-*.zip
thumbs
sql.log
shimmie.log
!lib/images
ext/admin
ext/amazon_s3

View File

@ -42,6 +42,8 @@ Installation
Upgrade from 2.3.X
~~~~~~~~~~~~~~~~~~
config.php has been moved from /config.php to /data/config/shimmie.conf.php
The database connection setting in config.php has changed; now using
PDO DSN format rather than ADODB URI:
@ -111,6 +113,3 @@ someone else, you have to give them the source (which should be easy, as PHP
is an interpreted language...). If you want to add customisations to your own
site, then those customisations belong to you, and you can do what you want
with them.

View File

@ -43,7 +43,7 @@ class AdminPage extends Extension {
global $page, $user;
if($event->page_matches("admin")) {
if(!$user->is_admin()) {
if(!$user->can("manage_admintools")) {
$this->theme->display_permission_denied();
}
else {
@ -76,7 +76,7 @@ class AdminPage extends Extension {
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("manage_admintools")) {
$event->add_link("Board Admin", make_link("admin"));
}
}
@ -90,7 +90,7 @@ class AdminPage extends Extension {
public function onPostListBuilding(PostListBuildingEvent $event) {
global $user;
if($user->is_admin() && !empty($event->search_terms)) {
if($user->can("manage_admintools") && !empty($event->search_terms)) {
$this->theme->display_dbq(implode(" ", $event->search_terms));
}
}
@ -167,7 +167,7 @@ class AdminPage extends Extension {
$zip = new ZipArchive;
$images = $database->get_all("SELECT * FROM images");
$filename = 'imgdump-'.date('Ymd').'.zip';
$filename = data_path('imgdump-'.date('Ymd').'.zip');
if($zip->open($filename, 1 ? ZIPARCHIVE::OVERWRITE:ZIPARCHIVE::CREATE) === TRUE){
foreach($images as $img){

View File

@ -26,7 +26,7 @@ class Downtime extends Extension {
global $config, $page, $user;
if($config->get_bool("downtime")) {
if(!$user->is_admin() && !$this->is_safe_page($event)) {
if(!$user->can("ignore_downtime") && !$this->is_safe_page($event)) {
$msg = $config->get_string("downtime_message");
$this->theme->display_message($msg);
exit;

View File

@ -29,7 +29,7 @@ class Featured extends Extension {
global $config, $page, $user;
if($event->page_matches("featured_image")) {
if($event->get_arg(0) == "set" && $user->check_auth_token()) {
if($user->is_admin() && isset($_POST['image_id'])) {
if($user->can("edit_feature") && isset($_POST['image_id'])) {
$id = int_escape($_POST['image_id']);
if($id > 0) {
$config->set_int("featured_id", $id);
@ -77,7 +77,7 @@ class Featured extends Extension {
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("edit_feature")) {
$event->add_part($this->theme->get_buttons_html($event->image->id));
}
}

View File

@ -56,7 +56,7 @@ class ImageBan extends Extension {
global $config, $database, $page, $user;
if($event->page_matches("image_hash_ban")) {
if($user->is_admin()) {
if($user->can("ban_image")) {
if($event->get_arg(0) == "dnp") {
$image = Image::by_id(int_escape($event->get_arg(1)));
if($image) {
@ -105,7 +105,7 @@ class ImageBan extends Extension {
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("ban_image")) {
$event->add_link("Image Bans", make_link("image_hash_ban/list/1"));
}
}
@ -120,7 +120,7 @@ class ImageBan extends Extension {
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("ban_image")) {
$event->add_part($this->theme->get_buttons_html($event->image));
}
}

View File

@ -26,24 +26,19 @@ class ImageBanTheme extends Themelet {
foreach($bans as $ban) {
$h_bans .= "
<tr>
<td width='30%'>{$ban['hash']}</td>
<td>{$ban['reason']}</td>
<td width='10%'>
".make_form(make_link("image_hash_ban/remove"))."
".make_form(make_link("image_hash_ban/remove"))."
<td width='30%'>{$ban['hash']}</td>
<td>{$ban['reason']}</td>
<td width='10%'>
<input type='hidden' name='hash' value='{$ban['hash']}'>
<input type='submit' value='Remove'>
</form>
</td>
</td>
</form>
</tr>
";
}
$html = "
<script type='text/javascript'>
$(document).ready(function() {
$(\"#image_bans\").tablesorter();
});
</script>
<table id='image_bans' class='zebra'>
<table id='image_bans' class='zebra sortable'>
<thead>
<th>Hash</th><th>Reason</th><th>Action</th>
<tr>

View File

@ -49,7 +49,7 @@ class IPBan extends Extension {
public function onPageRequest(PageRequestEvent $event) {
if($event->page_matches("ip_ban")) {
global $config, $database, $page, $user;
if($user->is_admin()) {
if($user->can("ban_ip")) {
if($event->get_arg(0) == "add" && $user->check_auth_token()) {
if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) {
if(empty($_POST['end'])) $end = null;
@ -80,7 +80,7 @@ class IPBan extends Extension {
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("ban_ip")) {
$event->add_link("IP Bans", make_link("ip_ban/list"));
}
}

View File

@ -44,7 +44,7 @@ class LogDatabase extends Extension {
public function onPageRequest(PageRequestEvent $event) {
global $database, $user;
if($event->page_matches("log/view")) {
if($user->is_admin()) {
if($user->can("view_eventlog")) {
$wheres = array();
$args = array();
$page_num = int_escape($event->get_arg(0));
@ -111,7 +111,7 @@ class LogDatabase extends Extension {
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("view_eventlog")) {
$event->add_link("Event Log", make_link("log/view"));
}
}

View File

@ -20,7 +20,7 @@ class LogDatabaseTheme extends Themelet {
</style>
<table class='zebra'>
<thead>
<tr><th>Time</th><th>Module</th><th>User</th><th>Message</th></tr>
<tr><th>Time</th><th>Module</th><th>User</th><th colspan='2'>Message</th></tr>
<form action='".make_link("log/view")."' method='GET'>
<tr class='sizedinputs'>
<td><input type='text' name='time' value='".$this->heie("time")."'></td>
@ -34,8 +34,8 @@ class LogDatabaseTheme extends Themelet {
<option value='".SCORE_LOG_ERROR."'>Error</option>
<option value='".SCORE_LOG_CRITICAL."'>Critical</option>
</select>
<input type='submit' value='Search' style='width: 20%'>
</td>
<td><input type='submit' value='Search'></td>
</tr>
</form>
</thead>
@ -56,7 +56,7 @@ class LogDatabaseTheme extends Themelet {
"<a href='".make_link("user/".url_escape($event['username']))."'>".html_escape($event['username'])."</a>".
"</span></td>";
}
$table .= "<td>".$this->scan_entities(html_escape($event['message']))."</td>";
$table .= "<td colspan='2'>".$this->scan_entities(html_escape($event['message']))."</td>";
$table .= "</tr>\n";
}
$table .= "</tbody></table>";

View File

@ -13,15 +13,17 @@ class LogNet extends Extension {
public function onLog(LogEvent $event) {
global $user;
if($this->count < 10 && $event->priority > 10) {
// TODO: colour based on event->priority
$username = ($user && $user->name) ? $user->name : "Anonymous";
$str = sprintf("%-15s %-10s: %s", $_SERVER['REMOTE_ADDR'], $username, $event->message);
system("echo ".escapeshellarg($str)." | nc -q 0 localhost 5000");
if($event->priority > 10) {
$this->count++;
}
else {
system("echo 'suppressing flood, check the web log' | nc -q 0 localhost 5000");
if($this->count < 10) {
// TODO: colour based on event->priority
$username = ($user && $user->name) ? $user->name : "Anonymous";
$str = sprintf("%-15s %-10s: %s", $_SERVER['REMOTE_ADDR'], $username, $event->message);
system("echo ".escapeshellarg($str)." | nc -q 0 localhost 5000");
}
else if($this->count == 10) {
system("echo 'suppressing flood, check the web log' | nc -q 0 localhost 5000");
}
}
}
}

View File

@ -38,7 +38,7 @@ class NumericScore extends Extension {
public function onUserPageBuilding(UserPageBuildingEvent $event) {
global $page, $user;
if($user->is_admin()) {
if($user->can("edit_other_votes")) {
$html = $this->theme->get_nuller_html($event->display_user);
$page->add_block(new Block("Votes", $html, "main", 60));
}
@ -79,7 +79,7 @@ class NumericScore extends Extension {
}
}
if($event->page_matches("numeric_score/remove_votes_on") && $user->check_auth_token()) {
if($user->is_admin()) {
if($user->can("edit_other_vote")) {
$image_id = int_escape($_POST['image_id']);
$database->execute(
"DELETE FROM numeric_score_votes WHERE image_id=?",
@ -92,16 +92,8 @@ class NumericScore extends Extension {
}
}
if($event->page_matches("numeric_score/remove_votes_by") && $user->check_auth_token()) {
if($user->is_admin()) {
$user_id = int_escape($_POST['user_id']);
$image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id));
$database->execute(
"DELETE FROM numeric_score_votes WHERE user_id=? AND image_id IN ?",
array($user_id, $image_ids));
$database->execute(
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=images.id) WHERE images.id IN ?",
array($image_ids));
if($user->can("edit_other_vote")) {
$this->delete_votes_by(int_escape($_POST['user_id']));
$page->set_mode("redirect");
$page->set_redirect(make_link());
}
@ -191,6 +183,31 @@ class NumericScore extends Extension {
$database->execute("DELETE FROM numeric_score_votes WHERE image_id=:id", array("id" => $event->image->id));
}
public function onUserDeletion(UserDeletionEvent $event) {
$this->delete_votes_by($event->id);
}
public function delete_votes_by(/*int*/ $user_id) {
global $database;
$image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id));
$database->execute(
"DELETE FROM numeric_score_votes WHERE user_id=? AND image_id IN (".implode(",", $image_ids).")",
array($user_id));
$database->execute("
UPDATE images
SET numeric_score=COALESCE(
(
SELECT SUM(score)
FROM numeric_score_votes
WHERE image_id=images.id
),
0
)
WHERE images.id IN (".implode(",", $image_ids).")");
}
// FIXME: on user deletion
// FIXME: on user vote nuke
@ -274,7 +291,12 @@ class NumericScore extends Extension {
array("imageid" => $image_id, "userid" => $user_id, "score" => $score));
}
$database->Execute(
"UPDATE images SET numeric_score=(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=:imageid) WHERE id=:id",
"UPDATE images SET numeric_score=(
COALESCE(
(SELECT SUM(score) FROM numeric_score_votes WHERE image_id=:imageid),
0
)
) WHERE id=:id",
array("imageid" => $image_id, "id" => $image_id));
}
}

View File

@ -30,7 +30,7 @@ class NumericScoreTheme extends Themelet {
<input type='submit' value='Vote Down'>
</form>
";
if($user->is_admin()) {
if($user->can("edit_other_vote")) {
$html .= "
<form action='".make_link("numeric_score/remove_votes_on")."' method='POST'>
".$user->get_auth_html()."

View File

@ -24,12 +24,10 @@ class Oekaki extends Extension {
if(isset($_FILES["picture"])) {
header('Content-type: text/plain');
$uploaddir = './data/oekaki_unclaimed/';
if(!file_exists($uploaddir)) mkdir($uploaddir, 0755, true);
$file = $_FILES['picture']['name'];
$ext = (strpos($file, '.') === FALSE) ? '' : substr($file, strrpos($file, '.'));
$uploadname = $_SERVER['REMOTE_ADDR'] . "." . time();
$uploadfile = $uploaddir . $uploadname;
$uploadfile = data_path('oekaki_unclaimed/'.$uploadname);
log_info("oekaki", "Uploading file [$uploadname]");
@ -53,7 +51,7 @@ class Oekaki extends Extension {
// FIXME: move .chi to data/oekaki/$ha/$hash mirroring images and thumbs
// FIXME: .chi viewer?
// FIXME: clean out old unclaimed images?
$pattern = './data/oekaki_unclaimed/' . $_SERVER['REMOTE_ADDR'] . ".*.png";
$pattern = data_path('oekaki_unclaimed/' . $_SERVER['REMOTE_ADDR'] . ".*.png");
foreach(glob($pattern) as $tmpname) {
assert(file_exists($tmpname));

View File

@ -90,7 +90,7 @@ class PrivMsg extends Extension {
global $page, $user;
$duser = $event->display_user;
if(!$user->is_anonymous() && !$duser->is_anonymous()) {
if(($user->id == $duser->id) || $user->is_admin()) {
if(($user->id == $duser->id) || $user->can("view_other_pms")) {
$this->theme->display_pms($page, $this->get_pms($duser));
}
if($user->id != $duser->id) {
@ -110,7 +110,7 @@ class PrivMsg extends Extension {
if(is_null($pm)) {
$this->theme->display_error(404, "No such PM", "There is no PM #$pm_id");
}
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
else if(($pm["to_id"] == $user->id) || $user->can("view_other_pms")) {
$from_user = User::by_id(int_escape($pm["from_id"]));
$database->execute("UPDATE private_message SET is_read='Y' WHERE id = :id", array("id" => $pm_id));
$database->cache->delete("pm-count-{$user->id}");
@ -127,7 +127,7 @@ class PrivMsg extends Extension {
if(is_null($pm)) {
$this->theme->display_error(404, "No such PM", "There is no PM #$pm_id");
}
else if(($pm["to_id"] == $user->id) || $user->is_admin()) {
else if(($pm["to_id"] == $user->id) || $user->can("view_other_pms")) {
$database->execute("DELETE FROM private_message WHERE id = :id", array("id" => $pm_id));
$database->cache->delete("pm-count-{$user->id}");
log_info("pm", "Deleted PM #$pm_id");

View File

@ -265,7 +265,6 @@ class ResizeImage extends Extension {
$new_hash = md5_file($tmp_filename);
$new_size = filesize($tmp_filename);
$target = warehouse_path("images", $new_hash);
if(!file_exists(dirname($target))) mkdir(dirname($target), 0755, true);
if(!@copy($tmp_filename, $target)) {
throw new ImageResizeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)");
}

View File

@ -97,29 +97,50 @@ class ShimmieApi extends Extension {
if($event->page_matches("api/shimmie/get_user")) {
$query = $user->id;
$type = "id";
if($event->count_args() == 1) {
$query = $event->get_arg(0);
}
if(isset($_GET['name'])) {
$query = $_GET['name'];
}
if(isset($_GET['id'])) {
elseif(isset($_GET['id'])) {
$query = $_GET['id'];
}
elseif(isset($_GET['name'])) {
$query = $_GET['name'];
$type = "name";
}
$all = $database->get_row(
"SELECT id,name,joindate,class FROM users WHERE name=? OR id=?",
array($_GET['name'], int_escape($_GET['id'])));
"SELECT id,name,joindate,class FROM users WHERE ".$type."=?",
array($query));
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
// - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...);
for($i=0; $i<4; $i++) unset($all[$i]);
$all['uploadcount'] = Image::count_images(array("user_id=".$all['id']));
$all['uploadperday'] = sprintf("%.1f", ($all['uploadcount'] / (((time() - strtotime($all['joindate'])) / 86400) + 1)));
$all['commentcount'] = $database->get_one(
"SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id",
array("owner_id"=>$all['id']));
$all['commentperday'] = sprintf("%.1f", ($all['commentcount'] / (((time() - strtotime($all['joindate'])) / 86400) + 1)));
if(!empty($all)){
//FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice..
// - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...);
for($i=0; $i<4; $i++) unset($all[$i]);
$all['uploadcount'] = Image::count_images(array("user_id=".$all['id']));
$all['commentcount'] = $database->get_one(
"SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id",
array("owner_id"=>$all['id']));
if(isset($_GET['recent'])){
$recent = $database->get_all(
"SELECT * FROM images WHERE owner_id=? ORDER BY id DESC LIMIT 0, 5",
array($all['id']));
$i = 0;
foreach($recent as $all['recentposts'][$i]){
unset($all['recentposts'][$i]['owner_id']); //We already know the owners id..
unset($all['recentposts'][$i]['owner_ip']);
for($x=0; $x<14; $x++) unset($all['recentposts'][$i][$x]);
if(empty($all['recentposts'][$i]['author'])) unset($all['recentposts'][$i]['author']);
if($all['recentposts'][$i]['notes'] > 0) $all['recentposts'][$i]['has_notes'] = "Y";
else $all['recentposts'][$i]['has_notes'] = "N";
unset($all['recentposts'][$i]['notes']);
$i += 1;
}
}
}
$page->set_data(json_encode($all));
}
}

View File

@ -35,7 +35,7 @@ class Tag_History extends Extension {
}
}
else if($event->page_matches("tag_history/bulk_revert")) {
if($user->is_admin() && $user->check_auth_token()) {
if($user->can("bulk_edit_image_tag") && $user->check_auth_token()) {
$this->process_bulk_revert_request();
}
}
@ -78,7 +78,7 @@ class Tag_History extends Extension {
public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
global $user;
if($user->is_admin()) {
if($user->can("bulk_edit_image_tag")) {
$event->add_link("Tag Changes", make_link("tag_history/all/1"));
}
}

View File

@ -27,6 +27,14 @@ class Tag_HistoryTheme extends Themelet {
$setter = "<a href='".make_link("user/".url_escape($name))."'>".html_escape($name)."</a>$h_ip";
$selected = ($n == 2) ? " checked" : "";
$current_tags = Tag::explode($current_tags);
$taglinks = array();
foreach($current_tags as $tag){
$taglinks[] = "<a href='".make_link("post/list/".$tag."/1")."'>".$tag."</a>";
}
$current_tags = Tag::implode($taglinks);
$history_list .= "
<li>
<input type='radio' name='revert' id='$current_id' value='$current_id'$selected>

View File

@ -91,7 +91,7 @@ class Update extends Extension {
$commit = $matches[2];
mkdir("./backup");
$html .= "<br>backup folder created!";
$d_dir = "data/cache";
$d_dir = data_path("cache");
//This should empty the /data/cache/ folder.
if (is_dir($d_dir)) {
$objects = scandir($d_dir);
@ -103,7 +103,7 @@ class Update extends Extension {
reset($objects);
$html .= "<br>data folder emptied!";
}
copy ("./config.php", "./backup/config.php");//Although this stays the same, will keep backup just incase.
copy ("./data/config/shimmie.conf.php", "./backup/shimmie.conf.php");//Although this stays the same, will keep backup just incase.
$folders = array("./core", "./lib", "./themes", "./.htaccess", "./doxygen.conf", "./index.php", "./install.php", "./ext", "./contrib");
foreach($folders as $folder){
//TODO: Check MD5 of each file, don't rename if same.

View File

@ -34,6 +34,7 @@ class BaseThemelet {
$h_view_link = make_link('post/view/'.$i_id, $query);
$h_thumb_link = $image->get_thumb_link();
$h_tip = html_escape($image->get_tooltip());
$h_tags = strtolower($image->get_tag_list());
$base = get_base_href();
// If file is flash or svg then sets thumbnail to max size.
@ -44,7 +45,7 @@ class BaseThemelet {
$tsize = get_thumbnail_size($image->width, $image->height);
}
return '<a href="'.$h_view_link.'" class="thumb">'.
return '<a href="'.$h_view_link.'" class="thumb" data-tags="'.$h_tags.'">'.
'<img id="thumb_'.$i_id.'" title="'.$h_tip.'" alt="'.$h_tip.'" height="'.$tsize[1].'" width="'.$tsize[0].'" class="lazy" data-original="'.$h_thumb_link.'" src="'.$base.'/lib/static/grey.gif">'.
'<noscript><img id="thumb_'.$i_id.'" title="'.$h_tip.'" alt="'.$h_tip.'" height="'.$tsize[1].'" width="'.$tsize[0].'" src="'.$h_thumb_link.'"></noscript>'.
"</a>\n";

View File

@ -273,7 +273,7 @@ class Database {
/**
* Create a new database object using connection info
* stored in config.php in the root shimmie folder
* stored in the config file
*/
public function Database() {
# FIXME: detect ADODB URI, automatically translate PDO DSN

View File

@ -2,7 +2,7 @@
/**
* These are the default configuration options for Shimmie.
*
* All of these can be over-ridden by placing a 'define' in config.php
* All of these can be over-ridden by placing a 'define' in data/config/shimmie.conf.php
*
* Do NOT change them in this file. These are the defaults only!
*

View File

@ -1080,7 +1080,6 @@ class Tag {
*/
function move_upload_to_archive(DataUploadEvent $event) {
$target = warehouse_path("images", $event->hash);
if(!file_exists(dirname($target))) mkdir(dirname($target), 0755, true);
if(!@copy($event->tmpname, $target)) {
$errors = error_get_last(); // note: requires php 5.2
throw new UploadException("Failed to copy file from uploads ({$event->tmpname}) to archive ($target): {$errors['type']} / {$errors['message']}");

View File

@ -245,222 +245,42 @@ class Page {
# 404/static handler will map these to themes/foo/bar.ico or lib/static/bar.ico
$this->add_html_header("<link rel='icon' type='image/x-icon' href='$data_href/favicon.ico'>");
$this->add_html_header("<link rel='apple-touch-icon' href='$data_href/apple-touch-icon.png'>");
/* Attempt to cache the CSS & JavaScript files */
if ($this->add_cached_auto_html_headers() === FALSE) {
// caching failed, add all files to html_headers.
foreach(glob("lib/*.css") as $css) {
$this->add_html_header('<link rel="stylesheet" href="'.mtimefile($css).'" type="text/css">');
}
$css_files = glob("ext/*/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$this->add_html_header('<link rel="stylesheet" href="'.mtimefile($css_file).'" type="text/css">');
}
}
$css_files = glob("themes/$theme_name/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$this->add_html_header('<link rel="stylesheet" href="'.mtimefile($css_file).'" type="text/css">');
}
}
foreach(glob("lib/*.js") as $js) {
$this->add_html_header('<script src="'.mtimefile($js).'" type="text/javascript"></script>');
}
$js_files = glob("ext/*/script.js");
if($js_files) {
foreach($js_files as $js_file) {
$this->add_html_header('<script src="'.mtimefile($js_file).'" type="text/javascript"></script>');
}
}
$css_files = array();
$css_latest = 0;
foreach(array_merge(zglob("lib/*.css"), zglob("ext/*/style.css"), zglob("themes/$theme_name/style.css")) as $css) {
$css_files[] = $css;
$css_latest = max($css_latest, filemtime($css));
}
}
/**
* Automatic caching of CSS and Javascript files
*
* Allows site admins to have Shimmie automatically cache and minify all CSS and JS files.
* This is done to reduce the number of HTTP requests (recommended by the Yahoo high-performance
* guidelines). It combines all of the CSS and JavaScript files into one for each type, and
* stores them in cached files to serve the client. Changes to the CSS or JavaScript files are
* caught by taking the md5sum of the concatenated files.
*
* Note: This can be somewhat problematic, as it edits the links to your CSS files (as well
* as the links to images inside them).
* Also, the directory cache directory needs to be writeable by the php/webserver user.
* PLEASE: Ensure that you test your site out throughly after enabling this module!
* Either that, or don't use this module unless you are sure of what it is doing.
*
* TODO: Add support for minify-ing CSS and Javascript files. (similar to Minify. See: http://code.google.com/p/minify/ or https://github.com/mrclay/minify)
* TODO: For performance reasons: Before performing the regex's, compute the md5 of the CSS & JS files and store somewhere to check later.
*
* @return
* This function returns FALSE if it failed to cache the files,
* and returns TRUE if it was successful.
*/
private function add_cached_auto_html_headers() {
global $config;
// store local copy for speed.
$autocache_css = $config->get_bool("autocache_css");
$autocache_js = $config->get_bool("autocache_js");
$theme_name = $config->get_string('theme', 'default');
if (!$autocache_css && !$autocache_js) {
return false; // caching disabled
}
$cache_location = $config->get_string("autocache_location", 'data/cache');
// Detect is there is a trailing slash, and add one if not.
$cache_location = ((strrpos($cache_location, '/') + 1) == strlen($cache_location)) ? $cache_location : $cache_location.'/';
// Create directory if needed.
if(!file_exists($cache_location)) {
if (!mkdir($cache_location, 0750, true)) {
return false; // failed to create directory
}
}
$data_href = get_base_href();
/* ----- CSS Files ----- */
if($autocache_css) {
// First get all the CSS from the lib directory
$contents_from_lib = '';
$css_files = glob("lib/*.css");
if($css_files) {
foreach($css_files as $css_file) {
$contents_from_lib .= file_get_contents($css_file);
}
// Can't directly cache the CSS files, as they might have relative locations to images, etc. in them.
// We have to adjust the URLs accordingly before saving the cached file.
$css_cache_file = data_path("cache/style.$css_latest.css");
if(!file_exists($css_cache_file)) {
$css_data = "";
foreach($css_files as $file) {
$file_data = file_get_contents($file);
$pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/';
$replace = 'url("../../lib/${1}")';
$contents_from_lib = preg_replace($pattern, $replace, $contents_from_lib);
}
// Next get all the CSS from the extensions
$contents_from_extensions = '';
$css_files = glob("ext/*/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$contents_from_extensions .= file_get_contents($css_file);
}
// Can't directly cache the CSS files, as they might have relative locations to images, etc. in them.
// We have to adjust the URLs accordingly before saving the cached file.
$pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/';
$replace = 'url("../../${1}")';
$contents_from_extensions = preg_replace($pattern, $replace, $contents_from_extensions);
}
// Get CSS from theme
$contents_from_theme = '';
$css_files = glob("themes/$theme_name/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$contents_from_theme .= file_get_contents($css_file);
}
// Can't directly cache the CSS files, as they might have relative locations to images, etc. in them.
// We have to adjust the URLs accordingly before saving the cached file.
$pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/';
$replace = 'url("../../${1}")';
$contents_from_theme = preg_replace($pattern, $replace, $contents_from_theme);
}
// Combine the three
$data = $contents_from_lib .' '. $contents_from_extensions .' '. $contents_from_theme;
// Minify the CSS if enabled.
if($config->get_bool("autocache_min_css")) {
// not supported yet.
// TODO: add support for Minifying CSS files.
$replace = 'url("../../'.dirname($file).'/$1")';
$file_data = preg_replace($pattern, $replace, $file_data);
$css_data .= $file_data . "\n";
}
file_put_contents($css_cache_file, $css_data);
}
$this->add_html_header("<link rel='stylesheet' href='$data_href/$css_cache_file' type='text/css'>");
// compute the MD5 sum of the concatenated CSS files
$md5sum = md5($data);
if(!file_exists($cache_location.$md5sum.'.css')) {
// remove any old cached CSS files.
$mask = '*.css';
array_map( 'unlink', glob( $mask ) );
// output the combined file
if(file_put_contents($cache_location.$md5sum.'.css', $data, LOCK_EX) === FALSE) {
return false; // failed to write the file
}
}
// tell the client where to get the css cache file
$this->add_html_header('<link rel="stylesheet" href="'.$data_href.'/'.$cache_location.$md5sum.'.css" type="text/css">');
$js_files = array();
$js_latest = 0;
foreach(array_merge(zglob("lib/*.js"), zglob("ext/*/script.js"), zglob("themes/$theme_name/script.js")) as $js) {
$js_files[] = $js;
$js_latest = max($js_latest, filemtime($js));
}
else {
// Caching of CSS disabled.
foreach(glob("lib/*.css") as $css) {
$this->add_html_header('<link rel="stylesheet" href="'.$data_href.'/'.$css.'" type="text/css">');
}
$css_files = glob("ext/*/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$this->add_html_header('<link rel="stylesheet" href="'.$data_href.'/'.$css_file.'" type="text/css">');
}
}
$css_files = glob("themes/$theme_name/style.css");
if($css_files) {
foreach($css_files as $css_file) {
$this->add_html_header('<link rel="stylesheet" href="'.$data_href.'/'.$css_file.'" type="text/css">');
}
$js_cache_file = data_path("cache/script.$js_latest.js");
if(!file_exists($js_cache_file)) {
$js_data = "";
foreach($js_files as $file) {
$js_data .= file_get_contents($file) . "\n";
}
file_put_contents($js_cache_file, $js_data);
}
/* ----- JavaScript Files ----- */
if($autocache_js) {
$data = '';
$js_files = glob("lib/*.js");
if($js_files) {
foreach($js_files as $js_file) {
$data .= file_get_contents($js_file);
}
}
$js_files = glob("ext/*/script.js");
if($js_files) {
foreach($js_files as $js_file) {
$data .= file_get_contents($js_file);
}
}
// Minify the JS if enabled.
if ($config->get_bool("autocache_min_js")){
// not supported yet.
// TODO: add support for Minifying JS files.
}
// compute the MD5 sum of the concatenated JavaScript files
$md5sum = md5($data);
if (!file_exists($cache_location.$md5sum.'.js')) {
// remove any old cached js files.
$mask = '*.js';
array_map( 'unlink', glob( $mask ) );
// output the combined file
if (file_put_contents($cache_location.$md5sum.'.js', $data, LOCK_EX) === FALSE) {
return false;
}
}
// tell the client where to get the js cache file
$this->add_html_header('<script src="'.$data_href.'/'.$cache_location.$md5sum.'.js" type="text/javascript"></script>');
}
else {
// Caching of Javascript disabled.
foreach(glob("lib/*.js") as $js) {
$this->add_html_header('<script src="'.$data_href.'/'.$js.'" type="text/javascript"></script>');
}
$js_files = glob("ext/*/script.js");
if($js_files) {
foreach($js_files as $js_file) {
$this->add_html_header('<script src="'.$data_href.'/'.$js_file.'" type="text/javascript"></script>');
}
}
}
return true;
$this->add_html_header("<script src='$data_href/$js_cache_file' type='text/javascript'></script>");
}
}
?>

View File

@ -40,7 +40,7 @@ class UserClass {
// object = image / user / tag / setting
new UserClass("base", null, array(
"change_setting" => False, # modify web-level settings, eg the config table
"override_config" => False, # modify sys-level settings, eg config.php
"override_config" => False, # modify sys-level settings, eg shimmie.conf.php
"big_search" => False, # search for more than 3 tags at once (speed mode only)
"manage_extension_list" => False,
@ -63,8 +63,14 @@ new UserClass("base", null, array(
"edit_image_source" => False,
"edit_image_owner" => False,
"edit_image_lock" => False,
"bulk_edit_image_tag" => False,
"delete_image" => False,
"ban_image" => False,
"view_eventlog" => False,
"ignore_downtime" => False,
"create_image_report" => False,
"view_image_report" => False, # deal with reported images
@ -73,6 +79,13 @@ new UserClass("base", null, array(
"manage_blocks" => False,
"manage_admintools" => False,
"view_other_pms" => False,
"edit_feature" => False,
"bulk_edit_vote" => False,
"edit_other_vote" => False,
"protected" => False, # only admins can modify protected users (stops a moderator changing an admin's password)
));
@ -100,6 +113,7 @@ new UserClass("admin", "base", array(
"delete_user" => True,
"create_image" => True,
"delete_image" => True,
"ban_image" => True,
"create_comment" => True,
"delete_comment" => True,
"replace_image" => True,
@ -108,12 +122,20 @@ new UserClass("admin", "base", array(
"edit_image_tag" => True,
"edit_image_source" => True,
"edit_image_owner" => True,
"bulk_edit_image_tag" => True,
"mass_tag_edit" => True,
"create_image_report" => True,
"view_image_report" => True,
"edit_wiki_page" => True,
"delete_wiki_page" => True,
"view_eventlog" => True,
"manage_blocks" => True,
"manage_admintools" => True,
"ignore_downtime" => True,
"view_other_pms" => True,
"edit_feature" => True,
"bulk_edit_vote" => True,
"edit_other_vote" => True,
"protected" => True,
));

View File

@ -361,6 +361,11 @@ function mtimefile($file) {
return "$data_href/$file?$mtime";
}
function zglob($pattern) {
$r = glob($pattern);
if($r) return $r;
else return array();
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
@ -640,6 +645,12 @@ function warehouse_path(/*string*/ $base, /*string*/ $hash, /*bool*/ $create=tru
return $pa;
}
function data_path($filename) {
$filename = "data/" . $filename;
if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true);
return $filename;
}
function transload($url, $mfile) {
global $config;
@ -879,9 +890,10 @@ $_event_listeners = array();
*/
function add_event_listener(Extension $extension, $pos=50, $events=array()) {
global $_event_listeners;
$pos *= 100;
foreach($events as $event) {
while(isset($_event_listeners[$event][$pos])) {
$pos++;
$pos += 1;
}
$_event_listeners[$event][$pos] = $extension;
}
@ -1024,8 +1036,8 @@ function _load_extensions() {
ctx_log_start("Loading extensions");
if(COMPILE_ELS && file_exists("data/event_listeners.php")) {
require_once("data/event_listeners.php");
if(COMPILE_ELS && file_exists("data/cache/event_listeners.php")) {
require_once("data/cache/event_listeners.php");
}
else {
foreach(get_declared_classes() as $class) {
@ -1069,7 +1081,7 @@ function _load_extensions() {
$p .= ");\n";
$p .= "?".">";
file_put_contents("data/event_listeners.php", $p);
file_put_contents(data_path("cache/event_listeners.php"), $p);
}
}
@ -1223,11 +1235,9 @@ function _start_cache() {
$_cache_hash = md5($_SERVER["QUERY_STRING"]);
$ab = substr($_cache_hash, 0, 2);
$cd = substr($_cache_hash, 2, 2);
$_cache_filename = "data/http_cache/$ab/$cd/$_cache_hash";
$_cache_filename = data_path("http_cache/$ab/$cd/$_cache_hash");
@chmod(data_path('http_cache'), 750);
if(!file_exists(dirname($_cache_filename))) {
mkdir(dirname($_cache_filename), 0750, true);
}
if(file_exists($_cache_filename) && (filemtime($_cache_filename) > time() - 3600)) {
$gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($_cache_filename)) . ' GMT';

View File

@ -640,11 +640,11 @@ RECURSIVE = YES
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE = config.php \
install.php \
EXCLUDE = install.php \
phpinfo.php \
contrib/simpletest/simpletest \
lib \
data \
images \
thumbs \
.git \

7
ext/alias_editor/main.php Executable file → Normal file
View File

@ -122,7 +122,7 @@ class AliasEditor extends Extension {
private function get_alias_csv(Database $database) {
$csv = "";
$aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases");
$aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases ORDER BY newtag");
foreach($aliases as $old => $new) {
$csv .= "$old,$new\n";
}
@ -138,5 +138,10 @@ class AliasEditor extends Extension {
}
}
}
// add alias *after* mass tag editing, else the MTE will
// search for the images and be redirected to the alias,
// missing out the images tagged with the oldtag
public function get_priority() {return 60;}
}
?>

View File

@ -33,7 +33,7 @@ class BBCode extends FormatterExtension {
) as $el) {
$text = preg_replace("!\[$el\](.*?)\[/$el\]!s", "<$el>$1</$el>", $text);
}
$text = preg_replace('!&gt;&gt;([^\d].+)!s', '<blockquote><small>$1</small></blockquote>', $text);
$text = preg_replace('!&gt;&gt;([^\d].+)!', '<blockquote><small>$1</small></blockquote>', $text);
$text = preg_replace('!&gt;&gt;(\d+)(#c?\d+)?!s', '<a class="shm-clink" data-clink-sel="$2" href="'.make_link('post/view/$1$2').'">&gt;&gt;$1$2</a>', $text);
$text = preg_replace('!\[url=site://(.*?)(#c\d+)?\](.*?)\[/url\]!s', '<a class="shm-clink" data-clink-sel="$2" href="'.make_link('$1$2').'">$3</a>', $text);
$text = preg_replace('!\[url\]site://(.*?)(#c\d+)?\[/url\]!s', '<a class="shm-clink" data-clink-sel="$2" href="'.make_link('$1$2').'">$1$2</a>', $text);

View File

@ -75,6 +75,19 @@ class PixelFileHandler extends DataHandlerExtension {
return $ok;
}
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) {
$event->add_part("
<form>
<select id='zoomer'>
<option value='full'>Full Size</option>
<option value='width'>Fit Width</option>
<option value='height'>Fit Height</option>
<option value='both'>Fit Both</option>
</select>
</form>
", 20);
}
// IM thumber {{{
private function make_thumb_convert(/*string*/ $inname, /*string*/ $outname) {

View File

@ -0,0 +1,40 @@
$(function() {
$("#zoomer").change(function(e) {
zoom(this.options[this.selectedIndex].value);
});
$("#main_image").click(function(e) {
switch($.cookie("ui-image-zoom")) {
case "full": zoom("width"); break;
default: zoom("full"); break;
}
});
if($.cookie("ui-image-zoom")) {
zoom($.cookie("ui-image-zoom"));
}
});
function zoom(zoom) {
var img = $('#main_image');
if(zoom == "full") {
img.css('max-width', img.data('width') + 'px');
img.css('max-height', img.data('height') + 'px');
}
if(zoom == "width") {
img.css('max-width', '95%');
img.css('max-height', img.data('height') + 'px');
}
if(zoom == "height") {
img.css('max-width', img.data('width') + 'px');
img.css('max-height', (window.innerHeight * 0.95) + 'px');
}
if(zoom == "both") {
img.css('max-width', '95%');
img.css('max-height', (window.innerHeight * 0.95) + 'px');
}
$("#zoomer").val(zoom);
$.cookie("ui-image-zoom", zoom, {path: '/', expires: 365});
}

View File

@ -5,7 +5,6 @@ class PixelFileHandlerTheme extends Themelet {
global $config;
$u_ilink = $image->get_image_link();
$html = "<img alt='main image' id='main_image' src='$u_ilink'>";
if($config->get_bool("image_show_meta") && function_exists("exif_read_data")) {
# FIXME: only read from jpegs?
$exif = @exif_read_data($image->get_image_filename(), 0, true);
@ -24,39 +23,8 @@ class PixelFileHandlerTheme extends Themelet {
}
}
$zoom_default = $config->get_bool("image_zoom", false) ? "scale(img);" : "";
$zoom = "<script type=\"text/javascript\">
img = document.getElementById(\"main_image\");
if(img) {
img.onclick = function() {scale(img);};
msg_div = document.createElement(\"div\");
msg_div.id = \"msg_div\";
msg_div.appendChild(document.createTextNode(\"Note: Image has been scaled to fit the screen; click to enlarge\"));
msg_div.style.display=\"none\";
img.parentNode.insertBefore(msg_div, img);
orig_width = $image->width;
$zoom_default
}
function scale(img) {
if(orig_width >= img.parentNode.clientWidth * 0.9) {
if(img.style.width != \"90%\") {
img.style.width = \"90%\";
msg_div.style.display = \"block\";
}
else {
img.style.width = orig_width + 'px';
msg_div.style.display = \"none\";
}
}
}
</script>";
$page->add_block(new Block("Image", $html.$zoom, "main", 10));
$html = "<img alt='main image' id='main_image' src='$u_ilink' data-width='{$image->width}' data-height='{$image->height}'>";
$page->add_block(new Block("Image", $html, "main", 10));
}
}
?>

View File

@ -298,11 +298,6 @@ class ImageIO extends Extension {
if(strlen(trim($image->source)) == 0) {
$image->source = null;
}
if(!empty($image->source)) {
if(!preg_match("#^(https?|ftp)://#", $image->source)) {
throw new ImageAdditionException("Image's source isn't a valid URL");
}
}
/*
* Check for an existing image
@ -431,11 +426,6 @@ class ImageIO extends Extension {
if(strlen(trim($image->source)) == 0) {
$image->source = $existing->get_source();
}
if(!empty($image->source)) {
if(!preg_match("#^(https?|ftp)://#", $image->source)) {
throw new ImageReplaceException("Image's source isn't a valid URL");
}
}
/*
This step could be optional, ie: perhaps move the image somewhere

30
ext/index/script.js Normal file
View File

@ -0,0 +1,30 @@
$(function() {
var blocked_tags = ($.cookie("ui-blocked-tags") || $.cookie("blocked-tags") || "").split(" ");
var themecheck = $(".thumb[data-tags~='tagme']").parent().attr('class');
var needs_refresh = false;
for(i in blocked_tags) {
var tag = blocked_tags[i];
if(tag) {
$(".thumb[data-tags~='"+tag+"']").hide();
if(themecheck == "thumbblock") {
$(".thumb[data-tags~='tagme']").parent().height(0); //required for lite theme
}
needs_refresh = true;
}
}
// need to trigger a reflow in opera, because opera implements
// text-align: justify with element margins and doesn't recalculate
// these margins when part of the line disappears...
if(needs_refresh) {
$('#image-list').hide();
$('#image-list').show();
}
});
function select_blocked_tags() {
var blocked_tags = prompt("Enter tags to ignore", $.cookie("ui-blocked-tags") || "My_Little_Pony");
if(blocked_tags) {
$.cookie("ui-blocked-tags", blocked_tags.toLowerCase(), {path: '/', expires: 365});
location.reload(true);
}
}

View File

@ -166,12 +166,6 @@ class Setup extends Extension {
$config->set_default_string("theme", "default");
$config->set_default_bool("word_wrap", true);
$config->set_default_bool("comment_captcha", false);
// Automatic caching is disabled by default
$config->set_default_string("autocache_location", "data/cache");
$config->set_default_bool("autocache_css", false);
$config->set_default_bool("autocache_jss", false);
$config->set_default_bool("autocache_min_css", false);
$config->set_default_bool("autocache_min_js", false);
}
public function onPageRequest(PageRequestEvent $event) {
@ -279,19 +273,6 @@ class Setup extends Extension {
$sb->add_text_option("api_recaptcha_privkey", "<br>Private key: ");
$sb->add_text_option("api_recaptcha_pubkey", "<br>Public key: ");
$event->panel->add_block($sb);
$sb = new SetupBlock("Automatic Caching of CSS & JS");
// the default is fine for just about everyone
//$sb->add_text_option("autocache_location", "Location: ");
//$sb->add_label("<br><i>This location needs to be writeable by the webserver.</i>");
$sb->add_bool_option("autocache_css", "Automatic caching of CSS: ");
$sb->add_bool_option("autocache_js", "<br>Automatic caching of JS: ");
// if the option does nothing, there's no point showing a
// "hey look, nothing!" message...
//$sb->add_bool_option("autocache_min_css", "<br>Minimize CSS files: ");
//$sb->add_bool_option("autocache_min_js", "<br>Minimize JS files: ");
$event->panel->add_block($sb);
}
public function onConfigSave(ConfigSaveEvent $event) {

View File

@ -187,6 +187,8 @@ class TagEdit extends Extension {
$search_set = Tag::explode($search);
$replace_set = Tag::explode($replace);
log_info("tag_edit", "Mass editing tags: '$search' -> '$replace'");
$last_id = -1;
while(true) {
// make sure we don't look at the same images twice.

View File

@ -45,11 +45,23 @@ class TagEditTest extends ShimmieWebTestCase {
function testMassEdit() {
$this->log_in_as_admin();
$image_id = $this->post_image("ext/simpletest/data/pbx_screenshot.jpg", "pbx");
$this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: pbx");
$this->get_page("admin");
$this->assert_text("Mass Tag Edit"); // just test it exists
$this->log_out();
$this->set_field("search", "pbx");
$this->set_field("replace", "pox");
$this->click("Set");
# FIXME: test mass tag editor
$this->get_page("post/view/$image_id");
$this->assert_title("Image $image_id: pox");
$this->delete_image($image_id);
$this->log_out();
}
}
?>

View File

@ -188,7 +188,7 @@ class TagList extends Extension {
$starts_with = $this->get_starts_with();
// check if we have a cached version
$cache_key = "data/tag_cloud-" . md5("tc" . $tags_min . $starts_with) . ".html";
$cache_key = data_path("cache/tag_cloud-" . md5("tc" . $tags_min . $starts_with) . ".html");
if(file_exists($cache_key)) {return file_get_contents($cache_key);}
// SHIT: PDO/pgsql has problems using the same named param twice -_-;;
@ -225,7 +225,7 @@ class TagList extends Extension {
$starts_with = $this->get_starts_with();
// check if we have a cached version
$cache_key = "data/tag_alpha-" . md5("ta" . $tags_min . $starts_with) . ".html";
$cache_key = data_path("cache/tag_alpha-" . md5("ta" . $tags_min . $starts_with) . ".html");
if(file_exists($cache_key)) {return file_get_contents($cache_key);}
$tag_data = $database->get_all($database->engine->scoreql_to_sql("
@ -261,7 +261,7 @@ class TagList extends Extension {
$tags_min = $this->get_tags_min();
// check if we have a cached version
$cache_key = "data/tag_popul-" . md5("tp" . $tags_min) . ".html";
$cache_key = data_path("cache/tag_popul-" . md5("tp" . $tags_min) . ".html");
if(file_exists($cache_key)) {return file_get_contents($cache_key);}
$tag_data = $database->get_all("

View File

@ -40,6 +40,14 @@ class UserCreationEvent extends Event {
}
}
class UserDeletionEvent extends Event {
var $id;
public function __construct($id) {
$this->id = $id;
}
}
class UserCreationException extends SCoreException {}
class UserPage extends Extension {
@ -519,6 +527,8 @@ class UserPage extends Extension {
);
}
send_event(new UserDeletionEvent($_POST['id']));
$database->execute(
"DELETE FROM users WHERE id = :id",
array("id" => $_POST['id'])

View File

@ -43,11 +43,11 @@
* Each of these can be imported at the start of a function with eg "global $page, $user;"
*/
if(!file_exists("config.php")) {
if(!file_exists("data/config/shimmie.conf.php")) {
header("Location: install.php");
exit;
}
require_once "config.php";
require_once "data/config/shimmie.conf.php";
require_once "core/default_config.inc.php";
require_once "core/util.inc.php";
require_once "lib/context.php";

View File

@ -64,13 +64,13 @@ require_once __SHIMMIE_ROOT__."core/database.class.php";
* This file lets anyone destroy the database -- disable it
* as soon as the admin is done installing for the first time
*/
if(is_readable("config.php")) {
if(is_readable("data/config/shimmie.conf.php")) {
session_start();
echo '<div id="iblock">';
echo '<h1>Shimmie Repair Console</h1>';
// Load the config
require_once __SHIMMIE_ROOT__."config.php"; // Load user/site specifics First
require_once __SHIMMIE_ROOT__."data/config/shimmie.conf.php"; // Load user/site specifics First
require_once __SHIMMIE_ROOT__."core/default_config.inc.php"; // Defaults for the rest.
if(
@ -120,7 +120,7 @@ if(is_readable("config.php")) {
else {
echo "
<h3>Login</h3>
<p>Enter the database DSN exactly as in config.php (ie, as originally installed) to access advanced recovery tools:</p>
<p>Enter the database DSN exactly as in shimmie.conf.php (ie, as originally installed) to access advanced recovery tools:</p>
<form action='install.php' method='POST'>
<center>
@ -403,14 +403,15 @@ function write_config() { // {{{
"define('DATABASE_DSN', '".DATABASE_DSN."');\n" .
'?' . '>';
if(is_writable("./") && file_put_contents("config.php", $file_content)) {
assert(file_exists("config.php"));
if(!file_exists("data/config")) {
mkdir("data/config", 0755, true);
}
else {
if(!file_put_contents("data/config/shimmie.conf.php", $file_content)) {
$h_file_content = htmlentities($file_content);
print <<<EOD
The web server isn't allowed to write to the config file; please copy
the text below, save it as 'config.php', and upload it into the shimmie
the text below, save it as 'data/config/shimmie.conf.php', and upload it into the shimmie
folder manually. Make sure that when you save it, there is no whitespace
before the "&lt;?php" or after the "?&gt;"

View File

@ -12,4 +12,4 @@
* Version: 1.7.2
*
*/
(function(a,b){$window=a(b),a.fn.lazyload=function(c){function f(){var b=0;d.each(function(){var c=a(this);if(e.skip_invisible&&!c.is(":visible"))return;if(!a.abovethetop(this,e)&&!a.leftofbegin(this,e))if(!a.belowthefold(this,e)&&!a.rightoffold(this,e))c.trigger("appear");else if(++b>e.failure_limit)return!1})}var d=this,e={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null};return c&&(undefined!==c.failurelimit&&(c.failure_limit=c.failurelimit,delete c.failurelimit),undefined!==c.effectspeed&&(c.effect_speed=c.effectspeed,delete c.effectspeed),a.extend(e,c)),$container=e.container===undefined||e.container===b?$window:a(e.container),0===e.event.indexOf("scroll")&&$container.bind(e.event,function(a){return f()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,c.one("appear",function(){if(!this.loaded){if(e.appear){var f=d.length;e.appear.call(b,f,e)}a("<img />").bind("load",function(){c.hide().attr("src",c.data(e.data_attribute))[e.effect](e.effect_speed),b.loaded=!0;var f=a.grep(d,function(a){return!a.loaded});d=a(f);if(e.load){var g=d.length;e.load.call(b,g,e)}}).attr("src",c.data(e.data_attribute))}}),0!==e.event.indexOf("scroll")&&c.bind(e.event,function(a){b.loaded||c.trigger("appear")})}),$window.bind("resize",function(a){f()}),f(),this},a.belowthefold=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.height()+$window.scrollTop():e=$container.offset().top+$container.height(),e<=a(c).offset().top-d.threshold},a.rightoffold=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.width()+$window.scrollLeft():e=$container.offset().left+$container.width(),e<=a(c).offset().left-d.threshold},a.abovethetop=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.scrollTop():e=$container.offset().top,e>=a(c).offset().top+d.threshold+a(c).height()},a.leftofbegin=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.scrollLeft():e=$container.offset().left,e>=a(c).offset().left+d.threshold+a(c).width()},a.inviewport=function(b,c){return!a.rightofscreen(b,c)&&!a.leftofscreen(b,c)&&!a.belowthefold(b,c)&&!a.abovethetop(b,c)},a.extend(a.expr[":"],{"below-the-fold":function(c){return a.belowthefold(c,{threshold:0,container:b})},"above-the-top":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-screen":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-screen":function(c){return!a.rightoffold(c,{threshold:0,container:b})},"in-viewport":function(c){return!a.inviewport(c,{threshold:0,container:b})},"above-the-fold":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-fold":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-fold":function(c){return!a.rightoffold(c,{threshold:0,container:b})}})})(jQuery,window)
(function(a,b){$window=a(b),a.fn.lazyload=function(c){function f(){var b=0;d.each(function(){var c=a(this);if(e.skip_invisible&&!c.is(":visible"))return;if(!a.abovethetop(this,e)&&!a.leftofbegin(this,e))if(!a.belowthefold(this,e)&&!a.rightoffold(this,e))c.trigger("appear");else if(++b>e.failure_limit)return!1})}var d=this,e={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null};return c&&(undefined!==c.failurelimit&&(c.failure_limit=c.failurelimit,delete c.failurelimit),undefined!==c.effectspeed&&(c.effect_speed=c.effectspeed,delete c.effectspeed),a.extend(e,c)),$container=e.container===undefined||e.container===b?$window:a(e.container),0===e.event.indexOf("scroll")&&$container.bind(e.event,function(a){return f()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,c.one("appear",function(){if(!this.loaded){if(e.appear){var f=d.length;e.appear.call(b,f,e)}a("<img />").bind("load",function(){c.hide().attr("src",c.data(e.data_attribute))[e.effect](e.effect_speed),b.loaded=!0;var f=a.grep(d,function(a){return!a.loaded});d=a(f);if(e.load){var g=d.length;e.load.call(b,g,e)}}).attr("src",c.data(e.data_attribute))}}),0!==e.event.indexOf("scroll")&&c.bind(e.event,function(a){b.loaded||c.trigger("appear")})}),$window.bind("resize",function(a){f()}),f(),this},a.belowthefold=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.height()+$window.scrollTop():e=$container.offset().top+$container.height(),e<=a(c).offset().top-d.threshold},a.rightoffold=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.width()+$window.scrollLeft():e=$container.offset().left+$container.width(),e<=a(c).offset().left-d.threshold},a.abovethetop=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.scrollTop():e=$container.offset().top,e>=a(c).offset().top+d.threshold+a(c).height()},a.leftofbegin=function(c,d){var e;return d.container===undefined||d.container===b?e=$window.scrollLeft():e=$container.offset().left,e>=a(c).offset().left+d.threshold+a(c).width()},a.inviewport=function(b,c){return!a.rightofscreen(b,c)&&!a.leftofscreen(b,c)&&!a.belowthefold(b,c)&&!a.abovethetop(b,c)},a.extend(a.expr[":"],{"below-the-fold":function(c){return a.belowthefold(c,{threshold:0,container:b})},"above-the-top":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-screen":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-screen":function(c){return!a.rightoffold(c,{threshold:0,container:b})},"in-viewport":function(c){return!a.inviewport(c,{threshold:0,container:b})},"above-the-fold":function(c){return!a.belowthefold(c,{threshold:0,container:b})},"right-of-fold":function(c){return a.rightoffold(c,{threshold:0,container:b})},"left-of-fold":function(c){return!a.rightoffold(c,{threshold:0,container:b})}})})(jQuery,window);

View File

@ -38,7 +38,7 @@ $(document).ready(function() {
});
try {
var sidebar_hidden = ($.cookie("sidebar-hidden") || "").split("|");
var sidebar_hidden = ($.cookie("ui-sidebar-hidden") || $.cookie("sidebar-hidden") || "").split("|");
for(var i in sidebar_hidden) {
if(sidebar_hidden[i].length > 0) {
$(sidebar_hidden[i]+" .blockbody").hide();
@ -63,7 +63,7 @@ $(document).ready(function() {
}
}
}
$.cookie("sidebar-hidden", sidebar_hidden.join("|"), {path: '/'});
$.cookie("ui-sidebar-hidden", sidebar_hidden.join("|"), {path: '/', expires: 365});
})
});

View File

@ -98,47 +98,5 @@ class CustomUserPageTheme extends UserPageTheme {
$page->disable_left();
parent::display_user_page($duser, $stats);
}
protected function build_options($duser) {
global $database;
global $config;
global $user;
$html = "";
$html .= "
<form action='".make_link("user_admin/change_pass")."' method='POST'>
<input type='hidden' name='name' value='{$duser->name}'>
<input type='hidden' name='id' value='{$duser->id}'>
<table style='width: 300px;'>
<tr><td colspan='2'>Change Password</td></tr>
<tr><td>Password</td><td><input type='password' name='pass1'></td></tr>
<tr><td>Repeat Password</td><td><input type='password' name='pass2'></td></tr>
<tr><td colspan='2'><input type='Submit' value='Change Password'></td></tr>
</table>
</form>
<p><form action='".make_link("user_admin/change_email")."' method='POST'>
<input type='hidden' name='id' value='{$duser->id}'>
<table style='width: 300px;'>
<tr><th colspan='2'>Change Email</th></tr>
<tr><td>Address</td><td><input type='text' name='address' value='".html_escape($duser->email)."'></td></tr>
<tr><td colspan='2'><input type='Submit' value='Set'></td></tr>
</table>
</form></p>
";
if($user->is_admin()) {
$i_user_id = int_escape($duser->id);
$h_is_admin = $duser->is_admin() ? " checked" : "";
$html .= "
<p>".make_form(make_link("user_admin/set_more"))."
<input type='hidden' name='id' value='$i_user_id'>
Admin: <input name='admin' type='checkbox'$h_is_admin>
<input type='submit' value='Set'>
</form>
";
}
return $html;
}
}
?>

View File

@ -70,7 +70,7 @@ a.tab:hover, a.tab:active, .tab-selected {
-moz-border-radius:4px;
-webkit-border-radius:4px;
}
.highlighted {
.lazy{
background:none repeat scroll 0 0 #CEDFF0;
padding:4px;
border:1px solid #C3D2E0;

View File

@ -6,28 +6,26 @@ class Themelet extends BaseThemelet {
*/
public function build_thumb_html(Image $image, $query=null) {
global $config;
$i_id = int_escape($image->id);
$h_view_link = make_link("post/view/$i_id", $query);
$i_id = (int) $image->id;
$h_view_link = make_link('post/view/'.$i_id, $query);
$h_thumb_link = $image->get_thumb_link();
$h_tip = html_escape($image->get_tooltip());
$h_tags = strtolower($image->get_tag_list());
$base = get_base_href();
// If file is flash or svg then sets thumbnail to max size.
if($image->ext == 'swf' || $image->ext == 'svg') {
if($image->ext === 'swf' || $image->ext === 'svg'){
$tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height'));
}
else {
else{
$tsize = get_thumbnail_size($image->width, $image->height);
}
return "
<center><div class='thumbblock'>
<a href='$h_view_link' style='position: relative; display: block; height: {$tsize[1]}px; width: {$tsize[0]}px;'>
<img id='thumb_$i_id' title='$h_tip' alt='$h_tip' class='highlighted' style='height: {$tsize[1]}px; width: {$tsize[0]}px;' src='$h_thumb_link'>
</a>
</div></center>
";
return '<center><div class="thumbblock">'.
'<a href="'.$h_view_link.'" class="thumb" data-tags="'.$h_tags.'">'.
'<img id="thumb_'.$i_id.'" title="'.$h_tip.'" alt="'.$h_tip.'" height="'.$tsize[1].'" width="'.$tsize[0].'" class="lazy" data-original="'.$h_thumb_link.'" src="'.$base.'/lib/static/grey.gif">'.
'<noscript><img id="thumb_'.$i_id.'" title="'.$h_tip.'" alt="'.$h_tip.'" height="'.$tsize[1].'" width="'.$tsize[0].'" src="'.$h_thumb_link.'"></noscript>'.
"</a></div></center>\n";
}

View File

@ -95,47 +95,5 @@ class CustomUserPageTheme extends UserPageTheme {
$page->disable_left();
parent::display_user_page($duser, $stats);
}
protected function build_options($duser) {
global $database;
global $config;
global $user;
$html = "";
$html .= "
<form action='".make_link("user_admin/change_pass")."' method='POST'>
<input type='hidden' name='name' value='{$duser->name}'>
<input type='hidden' name='id' value='{$duser->id}'>
<table style='width: 300px;'>
<tr><th colspan='2'>Change Password</th></tr>
<tr><td>Password</td><td><input type='password' name='pass1'></td></tr>
<tr><td>Repeat Password</td><td><input type='password' name='pass2'></td></tr>
<tr><td colspan='2'><input type='Submit' value='Change Password'></td></tr>
</table>
</form>
<p><form action='".make_link("user_admin/change_email")."' method='POST'>
<input type='hidden' name='id' value='{$duser->id}'>
<table style='width: 300px;'>
<tr><th colspan='2'>Change Email</th></tr>
<tr><td>Address</td><td><input type='text' name='address' value='".html_escape($duser->email)."'></td></tr>
<tr><td colspan='2'><input type='Submit' value='Set'></td></tr>
</table>
</form></p>
";
if($user->is_admin()) {
$i_user_id = int_escape($duser->id);
$h_is_admin = $duser->is_admin() ? " checked" : "";
$html .= "
<p>".make_form(make_link("user_admin/set_more"))."
<input type='hidden' name='id' value='$i_user_id'>
Admin: <input name='admin' type='checkbox'$h_is_admin>
<input type='submit' value='Set'>
</form>
";
}
return $html;
}
}
?>

View File

@ -13,7 +13,7 @@ class CustomViewImageTheme extends ViewImageTheme {
$page->add_html_header("<meta property=\"og:url\" content=\"".make_http(make_link("post/view/{$image->id}"))."\">");
$page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0));
$page->add_block(new Block("Statistics", $this->build_stats($image), "left", 15));
$page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10));
$page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 11));
$page->add_block(new Block(null, $this->build_pin($image), "main", 11));
}