Convert Image v1.1

AndyB

Well-known member
Purpose:

The purpose of this thread is to provide additional information on the Convert Image v1.0 add-on. To download and install this add-on please visit the following XenForo Resource:

http://xenforo.com/community/resources/convert-image.2521/

Description:

The Convert Image add-on will convert linked images using the [ IMG ] tags to attachments.

Key Features:
  • Converts images immediately to attachments after message is saved
  • Resizes images to maximum width and height settings
  • Setting for inserting full or thumbnail attachments
  • Setting for temporary image path
  • Setting for optional log file
Requirements:
  • Requires XenForo v1.2 and above
  • Requires ImageMagick to be installed on your server
Admin Settings:

pic001.webp
 
Last edited:
The directory structure

library
--Andy
----ConvertImage
------DataWriter.php
------Listener.php
 
library/Andy/ConvertImage/DataWriter.php (part 1)

This code is needs to be put into two posts because it exceeds the 10,000 character limit per post.

PHP:
<?php

class Andy_ConvertImage_DataWriter extends XFCP_Andy_ConvertImage_DataWriter
{
	public function save()
	{		
		// call parent function
		parent::save();
		
		// declare variable
		$tempImageDirectoryCheck = '';
		
		// check for imagemagick option
		$imPecl = XenForo_Application::get('options')->imageLibrary['class'];
		
		// get options from Admin CP -> Options -> Convert Image -> Temporary Image Directory   
		$temporaryImageDirectory = XenForo_Application::get('options')->temporaryImageDirectory;
		
		// verify temporary directory exists 
		if (file_exists($temporaryImageDirectory))
		{
			$tempImageDirectoryCheck = 'yes';
		}
		
		//#####################################
		// do basic checks before main loop
		// imagemagick must be installed and
		// temporary directory created
		//#####################################
		
		if ($imPecl == 'imPecl' AND $tempImageDirectoryCheck == 'yes')
		{
			// seacrh for [IMG] tag
			$pos1 = stripos($this->get('message'), '[img]');
		
			// run only if [IMG] tag is found
			if (is_numeric($pos1))
			{				
				// get current message
				$currentMessage = $this->get('message');
				
				// call mainLoop
				$this->mainLoop($currentMessage);				
			}
		}
	}
	
	public function mainLoop($currentMessage)
	{
		// define variables
		$done = '';
		$newMessage = '';
		$updateRequired = '';
		
		// start loop		
		while ($done != 'yes')
		{
			//#####################################
			// parse the message that was just saved
			// look for [IMG] and [/IMG] tags
			// each loop will process one image tag
			// when the entire message has been parsed
			// the $done variable will be set to 'yes'
			//#####################################
			
			// clear variables for each loop
			$imglink = '';
			$posStart = '';
			
			//#####################################
			// find [IMG] and [/IMG] tags
			//#####################################
			
			// first loop
			if ($newMessage == '') {
				$original_message = $currentMessage;
			}			
			
			// looping again
			if ($newMessage != '') {
				$original_message = $newMessage;
				$currentMessage = $newMessage;
			}
		
			$pos1 = stripos($currentMessage, '[IMG]');
		
			if ($pos1 === false) {
				$done = 'yes';
			}			
			
			// used later to replace with [ATTACH] code
			if ($posStart == '') {
				$posStart = $pos1;
			}
			
			// start at beginning of url
			$pos1 = $pos1 + 5;
			
			// delete $tempMessage up to beginning of image url
			$currentMessage = substr($currentMessage, $pos1);
			
			// get end of img tag 
			$pos1 = stripos($currentMessage, '[/IMG]');
			
			if ($pos1 === false) {
				$done = 'yes';
			}
			
			// $imglink will contain the link to the image
			$imglink = substr($currentMessage, 0 , $pos1);
		
			// only continue if we have an image link
			if ($imglink != '' AND $done != 'yes') 
			{
				//#####################################
				// download image into temporary directory
				//#####################################
				
				// get options from Admin CP -> Options -> Convert Image -> Temporary Image Directory   
				$temporaryImageDirectory = XenForo_Application::get('options')->temporaryImageDirectory;
				
				// define full path of download
				$tempFullPath = $temporaryImageDirectory . 'convert' . '_' . rand();
				
				if (!file_exists($tempFullPath)) {
					touch($tempFullPath); // create blank file
					chmod($tempFullPath,0777);
				}
		
				$ch = curl_init($imglink);
				$fp = fopen($tempFullPath, "w");
			
				curl_setopt($ch, CURLOPT_FILE, $fp);
				curl_setopt($ch, CURLOPT_HEADER, 0);
				curl_setopt($ch, CURLOPT_TIMEOUT, 5); 
				
				// execute curl
				curl_exec($ch);
				
				if (curl_errno($ch) > 0)
				{
					$done = 'yes';
				}				
				
				// close the file 
				fclose($fp);
				
				//#####################################
				// put image source into memory
				// later we will save image to attachments directory
				//#####################################
				
				$tempImage = file_get_contents($tempFullPath);
				
				//#####################################
				// make sure we have a valid image
				//#####################################

				// get dimensions - supress error message
				@list($width, $height) = getimagesize($tempFullPath);
				
				// if not image abort
				if (is_null($width))
				{
					$done = 'yes';	
				}
				
				// only contunue if we have a valid image
				if ($done != 'yes') 
				{				
					//#####################################
					// resize image
					//#####################################
					
					// get options from Admin CP -> Options -> Attachments -> Maximum Attachment Image Dimensions
					$attachmentMaxWidth = XenForo_Application::get('options')->attachmentMaxDimensions['width'];
					
					// get options from Admin CP -> Options -> Attachments -> Maximum Attachment Image Dimensions
					$attachmentMaxHeight = XenForo_Application::get('options')->attachmentMaxDimensions['height'];										

					// check if image width or height exceeds maximum allowed
					if ($width > $attachmentMaxWidth OR $height > $attachmentMaxHeight) {
						
						// resize only if width or height exceed maximum - (use the '\>' flag)
						exec("/usr/bin/convert $tempFullPath -resize $attachmentMaxWidth x $attachmentMaxHeight\> $tempFullPath");
						
						// get new image size
						list($width, $height) = getimagesize($tempFullPath);
					}				
					
					//#####################################
					// begin creating the attachment
					// prepare data for SQL query.
					//#####################################
					
					$currentUserId = XenForo_Visitor::getUserId();
					
					$uploadDate = time();
					$basename = basename($imglink);
					
					// make sure basename fits 100 character limit
					if (strlen($basename) > 100)
					{
						$strlen = strlen($basename);
						$pos_start = $strlen - 100;
						$basename = substr($basename,$pos_start);
					}
					
					$filesize = filesize($tempFullPath);
					$filehash = hash_file('md5', $tempFullPath);
					
					$currentPostId = $this->get('post_id');
					
					//#####################################
					// run SQL query (attachment_data)
					//#####################################			
					
					$db = XenForo_Application::get('db');
	
					$db->query("
						INSERT INTO xf_attachment_data
							(user_id, upload_date, filename, file_size, file_hash, width, height, thumbnail_width, thumbnail_height, attach_count)
						VALUES
							(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
					", array($currentUserId, $uploadDate, $basename, $filesize, $filehash, $width, $height, '0', '0', '1'));
					
					// get the new data_id number (auto incremented) 
					$dataId = $db->lastInsertId();
					
					//#####################################
					// run SQL query (attachment)
					//#####################################							
					
					$db->query("
						INSERT INTO xf_attachment
							(data_id, content_type, content_id, attach_date, temp_hash, unassociated, view_count)
						VALUES
							(?, ?, ?, ?, ?, ?, ?)
					", array($dataId, 'post', $currentPostId, $uploadDate, '', '0', '0'));
					
					// get the new attachment_id number (auto incremented) 
					$attachmentId = $db->lastInsertId();
 
Last edited:
library/Andy/ConvertImage/DataWriter.php (part 2)

This code is needs to be put into two posts because it exceeds the 10,000 character limit per post.

PHP:
					//#####################################
					// define last folder name 
					// 0-999 are stored in the 0 directory 
					// 1000-1999 stored in the 1 directory etc 
					//#####################################
					
					$lastfolder = floor($dataId / 1000);
					
					//#####################################
					// define full path
					//#####################################
					
					// get internal_data path
					$internalDataPath = XenForo_Helper_File::getInternalDataPath();
					
					// define full path
					$attachmentFullPath = $internalDataPath . '/attachments/' . $lastfolder . '/' . $dataId . '-' . $filehash . '.data';
			
					//#####################################
					// create new internal_data and data
					// directories if they don't exsist
					//#####################################
					
					$directory = $internalDataPath . '/attachments/' . $lastfolder;
					
					if (!file_exists($directory)) {
						
						// needed to make 0777
						$old_umask = umask(0);
						
						// make directories
						mkdir($externalDataPath . '/attachments/' . $lastfolder, 0777);
						mkdir($internalDataPath . '/attachments/' . $lastfolder, 0777);
						
						// create path for /data/index.html file
						$indexpath = $externalDataPath . '/attachments/' . $lastfolder . '/index.html';
						touch($indexpath); // Create index.html
						chmod($indexpath,0666);	
						
						// create path for /internal_data/index.html file
						$indexpath = $internalDataPath . '/attachments/' . $lastfolder . '/index.html';
						touch($indexpath); // Create index.html
						chmod($indexpath,0666);			
					}
							
					//#####################################
					// copy image from temporary directory 
					// to attachment directory and
					// delete temporary image
					//#####################################
					
					// prepare new file
					if (!file_exists($attachmentFullPath)) {
						touch($attachmentFullPath); // Create blank file
						chmod($attachmentFullPath,0777);
					}
								
					// save image to attachment directory
					file_put_contents($attachmentFullPath, $tempImage); 
					
					// remove temporary image
					exec("rm $tempFullPath");
					
					//#####################################
					// create thumbnail
					//#####################################
					
					// get data path					
					$externalDataPath = XenForo_Helper_File::getExternalDataPath();
			
					$thumbpath = $externalDataPath . '/attachments/' . $lastfolder . '/' . $dataId . '-' . $filehash . '.jpg';
						
					if (!file_exists($thumbpath)) {
						touch($thumbpath); // Create blank file
						chmod($thumbpath,0777);
					}
					
					// check if animated gif
					$isAnimation = exec("/usr/bin/identify -format '%n' $attachmentFullPath");	
					
					if ($isAnimation > 1)
					{	
						$firstFrame = $attachmentFullPath . '[0]';
						exec("/usr/bin/convert $firstFrame -coalesce -resize 100x100 $thumbpath");	
					}
					else
					{
						exec("/usr/bin/convert $attachmentFullPath -resize 100x100 $thumbpath");
					}
					
					//#####################################
					// update attachment_data table with dimensions
					//#####################################
					
					// get dimensions of thumbnail
					list($width, $height) = getimagesize($thumbpath);
					
                    $db->query('
                        UPDATE xf_attachment_data SET
                            thumbnail_width = ' . $width . ',
                            thumbnail_height = ' . $height . '
                        WHERE data_id = ' . $dataId . '
                    ');					
										
					//#####################################
					// update message with [ATTACH] tags
					//#####################################
					
					// get options from Admin CP -> Options -> Convert Image -> Insert Thumbnail    
					$insertThumbnail = XenForo_Application::get('options')->insertThumbnail;
					
					// insert attach code
					if ($insertThumbnail == 1)
					{
						$attachcode = '[ATTACH]' . $attachmentId . '[/ATTACH]';	
					}
					else
					{
						$attachcode = '[ATTACH=full]' . $attachmentId . '[/ATTACH]';	
					}
					
					// calulate end position	
					$posEnd = strlen($imglink) + 11;
					
					// create new message
					$newMessage = substr_replace($original_message, $attachcode, $posStart, $posEnd);
					
					//#####################################
					// update optional log file
					//#####################################
					
					// declare variable
					$convertImageLogFile = '';
					
					// get options from Admin CP -> Options -> Convert Image -> Log File   
					$convertImageLogFile = XenForo_Application::get('options')->convertImageLogFile;					
					
					if ($convertImageLogFile != '')
					{
						// verify log file exists
						if (file_exists($convertImageLogFile))
						{
							
							// adjust for local timezone
							$dateline = time() + XenForo_Locale::getTimeZoneOffset();
							
							// format date
							$formatedDate = date("m/d/y h:ia", $dateline);
							
							// prepare data
							$data = $formatedDate . ' / Post ' . $currentPostId . ' / ' . $imglink . '
';
							// update log file
							$handle = fopen($convertImageLogFile, 'a');
							fwrite($handle, $data);
							fclose($handle);
						}
					}
					
					//#####################################
					// set $updateRequired flag
					//#####################################														
					
					$updateRequired = 'yes';
				}
			}
		}
		
		if ($updateRequired == 'yes')
		{			
			//#####################################
			// update xf_post.message
			//#####################################			
			
			$db->query('
				UPDATE xf_post SET
					message = ?
				WHERE post_id = ?
			', array($newMessage, $currentPostId));							
			
			//#####################################
			// delete xf_bb_code_parse_cache
			//#####################################		
			
			$db->query('
				DELETE FROM xf_bb_code_parse_cache
				WHERE content_id = ?
			', $currentPostId);
			
			//#####################################
			// update ElasticSearch post index
			//#####################################
			
			// check if ElasticSearch is enabled
			if (XenForo_Application::get('options')->enableElasticsearch)
			{
				// get ElasticSearch options	
				$esServerOption = XenForo_Application::get('options')->elasticSearchServer;
				
				// get ElasticSearch port number
				$esPort = $esServerOption['port'];
				
				// get ElasticSearch index name
				$indexName = XenES_Api::getInstance()->getIndex();

				// delete current post data in ElasticSearch
				exec("curl -XDELETE 'http://localhost:" . $esPort . "/" . $indexName . "/post/'" . $currentPostId . "'");
				
				// prepare data
				$dataString = '{
					"message" : "' . $newMessage . '"
				}';
				
				$ch = curl_init('http://localhost:' . $esPort . '/' . $indexName . '/post/' . $currentPostId);
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
				
				curl_setopt($ch, CURLOPT_HTTPHEADER, array(                                                                          
				'Content-Type: application/json',                                                                                
				'Content-Length: ' . strlen($dataString))                                                                       
				); 
				 
				curl_setopt($ch, CURLOPT_POSTFIELDS, $dataString );
				curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
				
				// execute curl
				curl_exec($ch);
			}
		}
	}
}

?>
 
Last edited:
library/Andy/ConvertImage/Listener.php

PHP:
<?php

class Andy_ConvertImage_Listener
{	
	public static function loadClassDatawriter($class, array &$extend)
	{
		$extend[] = 'Andy_ConvertImage_DataWriter';
	} 
}

?>
 
Back
Top Bottom