* Link: http://www.yaoifox.com/
* License: GPLv2
* Description: Uploads images automatically using Cron Jobs
* Documentation: Installation guide: activate this extension and navigate to www.yoursite.com/cron_upload
*/
class CronUploader extends Extension {
// TODO: Checkbox option to only allow localhost + a list of additional IP adresses that can be set in /cron_upload
// TODO: Change logging to MySQL + display log at /cron_upload
// TODO: Move stuff to theme.php
/**
* Lists all log events this session
* @var string
*/
private $upload_info = "";
/**
* Lists all files & info required to upload.
* @var array
*/
private $image_queue = array();
/**
* Cron Uploader root directory
* @var string
*/
private $root_dir = "";
/**
* Key used to identify uploader
* @var string
*/
private $upload_key = "";
/**
* Checks if the cron upload page has been accessed
* and initializes the upload.
* @param PageRequestEvent $event
*/
public function onPageRequest(PageRequestEvent $event) {
global $config, $user;
if ($event->page_matches ( "cron_upload" )) {
$this->upload_key = $config->get_string ( "cron_uploader_key", "" );
// If the key is in the url, upload
if ($this->upload_key != "" && $event->get_arg ( 0 ) == $this->upload_key) {
// log in as admin
$this->process_upload(); // Start upload
}
else if ($user->is_admin()) {
$this->set_dir();
$this->display_documentation();
}
}
}
private function display_documentation() {
global $page;
$this->set_dir(); // Determines path to cron_uploader_dir
$queue_dir = $this->root_dir . "/queue";
$uploaded_dir = $this->root_dir . "/uploaded";
$failed_dir = $this->root_dir . "/failed_to_upload";
$queue_dirinfo = $this->scan_dir($queue_dir);
$uploaded_dirinfo = $this->scan_dir($uploaded_dir);
$failed_dirinfo = $this->scan_dir($failed_dir);
$cron_url = make_http(make_link("/cron_upload/" . $this->upload_key));
$cron_cmd = "curl --silent $cron_url";
$log_path = $this->root_dir . "/uploads.log";
$info_html = "Information
Directory
Files
Size (MB)
Directory Path
Queue
{$queue_dirinfo['total_files']}
{$queue_dirinfo['total_mb']}
Uploaded
{$uploaded_dirinfo['total_files']}
{$uploaded_dirinfo['total_mb']}
Failed
{$failed_dirinfo['total_files']}
{$failed_dirinfo['total_mb']}
Cron Command:
Create a cron job with the command above.
Read the documentation if you're not sure what to do. ";
$install_html = "
This cron uploader is fairly easy to use but has to be configured first.
1. Install & activate this plugin.
2. Upload your images you want to be uploaded to the queue directory using your FTP client.
($queue_dir)
This also supports directory names to be used as tags.
3. Go to the Board Config to the Cron Uploader menu and copy the Cron Command.
($cron_cmd)
4. Create a cron job or something else that can open a url on specified times.
If you're not sure how to do this, you can give the command to your web host and you can ask them to create the cron job for you.
When you create the cron job, you choose when to upload new images.
5. When the cron command is set up, your image queue will upload x file(s) at the specified times.
You can see any uploads or failed uploads in the log file. ($log_path)
Your uploaded images will be moved to the 'uploaded' directory, it's recommended that you remove everything out of this directory from time to time.
($uploaded_dir)
Whenever the url in that cron job command is opened, a new file will upload from the queue.
So when you want to manually upload an image, all you have to do is open the link once.
This link can be found under 'Cron Command' in the board config, just remove the 'wget ' part and only the url remains.
($cron_url)";
$block = new Block("Cron Uploader", $info_html, "main", 10);
$block_install = new Block("Installation Guide", $install_html, "main", 20);
$page->add_block($block);
$page->add_block($block_install);
}
public function onInitExt(InitExtEvent $event) {
global $config;
// Set default values
if ($config->get_string("cron_uploader_key", "")) {
$this->upload_key = $this->generate_key ();
$config->set_default_int ( 'cron_uploader_count', 1 );
$config->set_default_string ( 'cron_uploader_key', $this->upload_key );
$this->set_dir();
}
}
public function onSetupBuilding(SetupBuildingEvent $event) {
$this->set_dir();
$cron_url = make_http(make_link("/cron_upload/" . $this->upload_key));
$cron_cmd = "curl --silent $cron_url";
$documentation_link = make_http(make_link("cron_upload"));
$sb = new SetupBlock ( "Cron Uploader" );
$sb->add_label ( "Settings " );
$sb->add_int_option ( "cron_uploader_count", "How many to upload each time" );
$sb->add_text_option ( "cron_uploader_dir", " Set Cron Uploader root directory ");
$sb->add_label (" Cron Command:
Create a cron job with the command above. Read the documentation if you're not sure what to do.");
$event->panel->add_block ( $sb );
}
/*
* Generates a unique key for the website to prevent unauthorized access.
*/
private function generate_key() {
$length = 20;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for($i = 0; $i < $length; $i ++) {
$randomString .= $characters [rand ( 0, strlen ( $characters ) - 1 )];
}
return $randomString;
}
/*
* Set the directory for the image queue. If no directory was given, set it to the default directory.
*/
private function set_dir() {
global $config;
// Determine directory (none = default)
$dir = $config->get_string("cron_uploader_dir", "");
// Sets new default dir if not in config yet/anymore
if ($dir == "") {
$dir = data_path("cron_uploader");
$config->set_string ('cron_uploader_dir', $dir);
}
// Make the directory if it doesn't exist yet
if (!is_dir($dir . "/queue/"))
mkdir ( $dir . "/queue/", 0775, true );
if (!is_dir($dir . "/uploaded/"))
mkdir ( $dir . "/uploaded/", 0775, true );
if (!is_dir($dir . "/failed_to_upload/"))
mkdir ( $dir . "/failed_to_upload/", 0775, true );
$this->root_dir = $dir;
return $dir;
}
/**
* Returns amount of files & total size of dir.
* @param $path string
* @return multitype:number
*/
function scan_dir($path){
$ite=new RecursiveDirectoryIterator($path);
$bytestotal=0;
$nbfiles=0;
foreach (new RecursiveIteratorIterator($ite) as $filename=>$cur) {
$filesize = $cur->getSize();
$bytestotal += $filesize;
$nbfiles++;
}
$size_mb = $bytestotal / 1048576; // to mb
$size_mb = number_format($size_mb, 2, '.', '');
return array('total_files'=>$nbfiles,'total_mb'=>$size_mb);
}
/**
* Uploads the image & handles everything
* @param $upload_count int to upload a non-config amount of imgs
* @return boolean returns true if the upload was successful
*/
public function process_upload($upload_count = 0) {
global $config;
set_time_limit(0);
$this->set_dir();
$this->generate_image_queue();
// Gets amount of imgs to upload
if ($upload_count == 0) $upload_count = $config->get_int ("cron_uploader_count", 1);
// Throw exception if there's nothing in the queue
if (count($this->image_queue) == 0) {
$this->add_upload_info("Your queue is empty so nothing could be uploaded.");
$this->handle_log();
return false;
}
// Randomize Images
shuffle($this->image_queue);
// Upload the file(s)
for ($i = 0; $i < $upload_count; $i++) {
$img = $this->image_queue[$i];
try {
$this->add_image($img[0], $img[1], $img[2]);
$this->move_uploaded($img[0], $img[1], false);
}
catch (Exception $e) {
$this->move_uploaded($img[0], $img[1], true);
}
// Remove img from queue array
unset($this->image_queue[$i]);
}
// Display & save upload log
$this->handle_log();
return true;
}
private function move_uploaded($path, $filename, $corrupt = false) {
global $config;
// Create
$newDir = $this->root_dir;
// Determine which dir to move to
if ($corrupt) {
// Move to corrupt dir
$newDir .= "/failed_to_upload/";
$info = "ERROR: Image was not uploaded.";
}
else {
$newDir .= "/uploaded/";
$info = "Image successfully uploaded. ";
}
// move file to correct dir
rename($path, $newDir.$filename);
$this->add_upload_info($info . "Image \"$filename\" moved from queue to \"$newDir\".");
}
/**
* Generate the necessary DataUploadEvent for a given image and tags.
*/
private function add_image($tmpname, $filename, $tags) {
assert ( file_exists ( $tmpname ) );
$pathinfo = pathinfo ( $filename );
if (! array_key_exists ( 'extension', $pathinfo )) {
throw new UploadException ( "File has no extension" );
}
$metadata = array();
$metadata ['filename'] = $pathinfo ['basename'];
$metadata ['extension'] = $pathinfo ['extension'];
$metadata ['tags'] = ""; // = $tags; doesn't work when not logged in here
$metadata ['source'] = null;
$event = new DataUploadEvent ( $tmpname, $metadata );
send_event ( $event );
// Generate info message
$infomsg = ""; // Will contain info message
if ($event->image_id == -1)
$infomsg = "File type not recognised. Filename: {$filename}";
else $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}";
$msgNumber = $this->add_upload_info($infomsg);
// Set tags
$img = Image::by_id($event->image_id);
$img->set_tags($tags);
}
private function generate_image_queue($base = "", $subdir = "") {
if ($base == "")
$base = $this->root_dir . "/queue";
if (! is_dir ( $base )) {
$this->add_upload_info("Image Queue Directory could not be found at \"$base\".");
return array();
}
foreach ( glob ( "$base/$subdir/*" ) as $fullpath ) {
$fullpath = str_replace ( "//", "/", $fullpath );
//$shortpath = str_replace ( $base, "", $fullpath );
if (is_link ( $fullpath )) {
// ignore
} else if (is_dir ( $fullpath )) {
$this->generate_image_queue ( $base, str_replace ( $base, "", $fullpath ) );
} else {
$pathinfo = pathinfo ( $fullpath );
$matches = array ();
if (preg_match ( "/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches )) {
$tags = $matches [1];
} else {
$tags = $subdir;
$tags = str_replace ( "/", " ", $tags );
$tags = str_replace ( "__", " ", $tags );
if ($tags == "") $tags = " ";
$tags = trim ( $tags );
}
$img = array (
0 => $fullpath,
1 => $pathinfo ["basename"],
2 => $tags
);
array_push ($this->image_queue, $img );
}
}
}
/**
* Adds a message to the info being published at the end
* @param $text string
* @param $addon int Enter a value to modify an existing value (enter value number)
* @return int
*/
private function add_upload_info($text, $addon = 0) {
$info = $this->upload_info;
$time = "[" .date('Y-m-d H:i:s'). "]";
// If addon function is not used
if ($addon == 0) {
$this->upload_info .= "$time $text\r\n";
// Returns the number of the current line
$currentLine = substr_count($this->upload_info, "\n") -1;
return $currentLine;
}
// else if addon function is used, select the line & modify it
$lines = substr($info, "\n"); // Seperate the string to array in lines
$lines[$addon] = "$lines[$addon] $text"; // Add the content to the line
$this->upload_info = implode("\n", $lines); // Put string back together & update
return $addon; // Return line number
}
/**
* This is run at the end to display & save the log.
*/
private function handle_log() {
global $page;
// Display message
$page->set_mode("data");
$page->set_type("text/plain");
$page->set_data($this->upload_info);
// Save log
$log_path = $this->root_dir . "/uploads.log";
if (file_exists($log_path))
$prev_content = file_get_contents($log_path);
else $prev_content = "";
$content = $prev_content ."\r\n".$this->upload_info;
file_put_contents ($log_path, $content);
}
}