Project

General

Profile

« Previous | Next » 

Revision 1797

Added by Dietmar about 12 years ago

+ add new image class to admin/media for better images preview

View differences:

branches/2.8.x/CHANGELOG
13 13

  
14 14

  
15 15

  
16
27 Oct-2012 Build 1797 Dietmar Woellbrink (Luisehahne)
17
+ add new image class to admin/media for better images preview
16 18
24 Oct-2012 Build 1796 Dietmar Woellbrink (Luisehahne)
17 19
! supress frontend preferences, if user have no permission for it
18 20
24 Oct-2012 Build 1795 Dietmar Woellbrink (Luisehahne)
branches/2.8.x/wb/admin/media/thumb.php
1
<?php
2
/**
3
 *
4
 * @category        admin
5
 * @package         admintools
6
 * @author          WebsiteBaker Project
7
 * @copyright       2004-2009, Ryan Djurovich
8
 * @copyright       2009-2011, Website Baker Org. e.V.
9
 * @link			http://www.websitebaker2.org/
10
 * @license         http://www.gnu.org/licenses/gpl.html
11
 * @platform        WebsiteBaker 2.8.x
12
 * @requirements    PHP 5.2.2 and higher
13
 * @version         $Id$
14
 * @filesource		$HeadURL:  $
15
 * @lastmodified    $Date:  $
16
 *
17
 */
18

  
19
require('../../config.php');
20
include_once('resize_img.php');
21
require_once(WB_PATH.'/framework/functions.php');
22

  
23
if (isset($_GET['img']) && isset($_GET['t'])) {
24
	$image = addslashes($_GET['img']);
25

  
26
	// Check to see if it contains ..
27
	if (!check_media_path($image)) {
28
		$admin->print_error($MESSAGE['MEDIA']['DIR_DOT_DOT_SLASH'], WB_URL, false);
29
	}
30

  
31
	$type = addslashes($_GET['t']);
32
	$media = WB_PATH.MEDIA_DIRECTORY;
33
	$img=new RESIZEIMAGE($media.$image);
34
	if ($img->imgWidth) {
35
		if ($type == 1) {
36
			$img->resize_limitwh(50,50);
37
		} else if ($type == 2) {
38
			$img->resize_limitwh(200,200);
39
		} 
40
		$img->close();
41
	} else {
42
		header ("Content-type: image/jpeg");
43
		readfile ( "nopreview.jpg" );
44
	}
45
}
46
?>
47 0

  
branches/2.8.x/wb/admin/media/browse.php
17 17

  
18 18
// Create admin object
19 19
require('../../config.php');
20
require_once(WB_PATH.'/framework/class.admin.php');
20
if(!class_exists('admin', false)){ include(WB_PATH.'/framework/class.admin.php'); }
21 21
$admin = new admin('Media', 'media', false);
22 22

  
23 23
$starttime = explode(" ", microtime());
......
66 66
	}
67 67
}
68 68

  
69
function ShowTip($name,$detail='') {
70
$parts = explode(".", $name);
71
$ext = strtolower(end($parts));
72
if (strpos('.gif.jpg.jpeg.png.bmp.',$ext) )
73
	return 'onmouseover="overlib(\'<img src=\\\''.$name.'\\\' maxwidth=\\\'200\\\' maxheight=\\\'200\\\'>\',VAUTO, WIDTH)" onmouseout="nd()" ' ;
74
else
75
	return '';
69
function ToolTip($name, $detail = '')
70
{
71
//    parse_str($name, $array);
72
//    $name = $array['img'];
73
    $parts = explode(".", $name);
74
    $ext = strtolower( end($parts));
75
    if (strpos('.gif.jpg.jpeg.png.bmp.', $ext))
76
	{
77
        $retVal = 'onmouseover="return overlib('.
78
            '\'<img src=\\\''.($name).'\\\''.
79
            'maxwidth=\\\'300\\\' '.
80
            'maxheight=\\\'300\\\'>\','.
81
//            '>\','.
82
            'CAPTION,\''.basename($name).'\','.
83
            'FGCOLOR,\'#ffffff\','.
84
            'BGCOLOR,\'#557c9e\','.
85
            'BORDER,1,'.
86
            'FGCOLOR, \'#ffffff\','.
87
            'BGCOLOR,\'#557c9e\','.
88
            'CAPTIONSIZE,\'12px\','.
89
            'CLOSETEXT,\'X\','.
90
            'CLOSECOLOR,\'#ffffff\','.
91
            'CLOSESIZE,\'14px\','.
92
            'VAUTO,'.
93
            'HAUTO,'.
94
            ''.
95
            'STICKY,'.
96
            'MOUSEOFF,'.
97
            'WRAP,'.
98
            'CELLPAD,5'.
99
            ''.
100
            ''.
101
            ''.
102
            ')" onmouseout="return nd()"';
103
        return $retVal;
104
//        return ('onmouseover="return overlib(\'<img src=\\\''.($name).'\\\' maxwidth=\\\'600\\\'  maxheight=\\\'600\\\'>\',BORDER,1,FGCOLOR, \'#ffffff\',VAUTO,WIDTH)" onmouseout="return nd()" ');
105
    } else {
106
        return '';
107
    }
76 108
}
77 109

  
78 110
function fsize($size) {
......
239 271
				$info = getimagesize(WB_PATH.MEDIA_DIRECTORY.$directory.'/'.$name);
240 272
				if ($info[0]) {
241 273
					$imgdetail = fsize(filesize(WB_PATH.MEDIA_DIRECTORY.$directory.'/'.$name)).'<br /> '.$info[0].' x '.$info[1].' px';
242
					$icon = 'thumb.php?t=1&amp;img='.$directory.'/'.$name;
243
					$tooltip = ShowTip('thumb.php?t=2&amp;img='.$directory.'/'.$name);
274
					$icon = 'thumbs.php?t=1&img='.$directory.'/'.$name;
275
					$tooltip = ToolTip('thumbs.php?t=2&img='.$directory.'/'.$name);
244 276
				}
245 277
			}
246 278

  
branches/2.8.x/wb/admin/media/thumbs.php
1
<?php
2
/**
3
 *
4
 * @category        admin
5
 * @package         media
6
 * @author          Ryan Djurovich (2004-2009), WebsiteBaker Project
7
 * @copyright       2009-2012, WebsiteBaker Org. e.V.
8
 * @link            http://www.websitebaker2.org/
9
 * @license         http://www.gnu.org/licenses/gpl.html
10
 * @platform        WebsiteBaker 2.8.x
11
 * @requirements    PHP 5.2.2 and higher
12
 * @version         $Id$
13
 * @filesource      $HeadURL$
14
 * @lastmodified    $Date$
15
 *
16
 */
17

  
18
require('../../config.php');
19
$modulePath = dirname(__FILE__);
20

  
21
/*
22
// Get image
23
	$requestMethod = '_'.strtoupper($_SERVER['REQUEST_METHOD']);
24
	$image = (isset(${$requestMethod}['img']) ? ${$requestMethod}['img'] : '');
25
print '<pre style="text-align: left;"><strong>function '.__FUNCTION__.'( '.''.' );</strong>  basename: '.basename(__FILE__).'  line: '.__LINE__.' -> <br />';
26
print_r( $_GET ); print '</pre>';  die(); // flush ();sleep(10);
27
*/
28

  
29
if (isset($_GET['img']) && isset($_GET['t'])) {
30
    if(!class_exists('PhpThumbFactory', false)){ include($modulePath.'/inc/ThumbLib.inc.php'); }
31
//	require_once($modulePath.'/inc/ThumbLib.inc.php');
32
	$image = addslashes($_GET['img']);
33
	$type = intval($_GET['t']);
34
//	$media = WB_PATH.MEDIA_DIRECTORY.'/';
35
	$thumb = PhpThumbFactory::create(WB_PATH.MEDIA_DIRECTORY.'/'.$image);
36

  
37
	if ($type == 1) {
38
//		$thumb->cropFromCenter(80,50);
39
        $thumb->resize(30,30);
40
// 		$thumb->resizePercent(40);
41
	} else {
42
    	$thumb->resize(500,500);
43
// 		$thumb->resizePercent(50);
44
//		$thumb->cropFromCenter(80,50);
45
	}
46
	$thumb->show();
47

  
48
 }
0 49

  
branches/2.8.x/wb/admin/media/inc/thumb_plugins/gd_reflection.inc.php
1
<?php
2
/**
3
 * GD Reflection Lib Plugin Definition File
4
 * 
5
 * This file contains the plugin definition for the GD Reflection Lib for PHP Thumb
6
 * 
7
 * PHP Version 5 with GD 2.0+
8
 * PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
9
 * Copyright (c) 2009, Ian Selby/Gen X Design
10
 * 
11
 * Author(s): Ian Selby <ian@gen-x-design.com>
12
 * 
13
 * Licensed under the MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 * 
16
 * @author Ian Selby <ian@gen-x-design.com>
17
 * @copyright Copyright (c) 2009 Gen X Design
18
 * @link http://phpthumb.gxdlabs.com
19
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
20
 * @version 3.0
21
 * @package PhpThumb
22
 * @filesource
23
 */
24

  
25
/**
26
 * GD Reflection Lib Plugin
27
 * 
28
 * This plugin allows you to create those fun Apple(tm)-style reflections in your images
29
 * 
30
 * @package PhpThumb
31
 * @subpackage Plugins
32
 */
33
class GdReflectionLib
34
{
35
	/**
36
	 * Instance of GdThumb passed to this class
37
	 * 
38
	 * @var GdThumb
39
	 */
40
	protected $parentInstance;
41
	protected $currentDimensions;
42
	protected $workingImage;
43
	protected $newImage;
44
	protected $options;
45
	
46
	public function createReflection ($percent, $reflection, $white, $border, $borderColor, &$that)
47
	{
48
		// bring stuff from the parent class into this class...
49
		$this->parentInstance 		= $that;
50
		$this->currentDimensions 	= $this->parentInstance->getCurrentDimensions();
51
		$this->workingImage			= $this->parentInstance->getWorkingImage();
52
		$this->newImage				= $this->parentInstance->getOldImage();
53
		$this->options				= $this->parentInstance->getOptions();
54
		
55
		$width				= $this->currentDimensions['width'];
56
		$height				= $this->currentDimensions['height'];
57
		$reflectionHeight 	= intval($height * ($reflection / 100));
58
		$newHeight			= $height + $reflectionHeight;
59
		$reflectedPart		= $height * ($percent / 100);
60
		
61
		$this->workingImage = imagecreatetruecolor($width, $newHeight);
62
		
63
		imagealphablending($this->workingImage, true);
64
		
65
		$colorToPaint = imagecolorallocatealpha($this->workingImage,255,255,255,0);
66
        imagefilledrectangle($this->workingImage,0,0,$width,$newHeight,$colorToPaint);
67
		
68
		imagecopyresampled
69
		(
70
            $this->workingImage,
71
            $this->newImage,
72
            0,
73
            0,
74
            0,
75
            $reflectedPart,
76
            $width,
77
            $reflectionHeight,
78
            $width,
79
            ($height - $reflectedPart)
80
		);
81
		
82
		$this->imageFlipVertical();
83
		
84
		imagecopy($this->workingImage, $this->newImage, 0, 0, 0, 0, $width, $height);
85
		
86
		imagealphablending($this->workingImage, true);
87
		
88
		for ($i = 0; $i < $reflectionHeight; $i++) 
89
		{
90
            $colorToPaint = imagecolorallocatealpha($this->workingImage, 255, 255, 255, ($i/$reflectionHeight*-1+1)*$white);
91
			
92
            imagefilledrectangle($this->workingImage, 0, $height + $i, $width, $height + $i, $colorToPaint);
93
        }
94
		
95
		if($border == true) 
96
		{
97
            $rgb 			= $this->hex2rgb($borderColor, false);
98
            $colorToPaint 	= imagecolorallocate($this->workingImage, $rgb[0], $rgb[1], $rgb[2]);
99
			
100
            imageline($this->workingImage, 0, 0, $width, 0, $colorToPaint); //top line
101
            imageline($this->workingImage, 0, $height, $width, $height, $colorToPaint); //bottom line
102
            imageline($this->workingImage, 0, 0, 0, $height, $colorToPaint); //left line
103
            imageline($this->workingImage, $width-1, 0, $width-1, $height, $colorToPaint); //right line
104
        }
105
		
106
		if ($this->parentInstance->getFormat() == 'PNG')
107
		{
108
			$colorTransparent = imagecolorallocatealpha
109
			(
110
				$this->workingImage, 
111
				$this->options['alphaMaskColor'][0], 
112
				$this->options['alphaMaskColor'][1], 
113
				$this->options['alphaMaskColor'][2], 
114
				0
115
			);
116
			
117
			imagefill($this->workingImage, 0, 0, $colorTransparent);
118
			imagesavealpha($this->workingImage, true);
119
		}
120
		
121
		$this->parentInstance->setOldImage($this->workingImage);
122
		$this->currentDimensions['width'] 	= $width;
123
		$this->currentDimensions['height']	= $newHeight;
124
		$this->parentInstance->setCurrentDimensions($this->currentDimensions);
125
		
126
		return $that;
127
	}
128
	
129
	/**
130
	 * Flips the image vertically
131
	 * 
132
	 */
133
	protected function imageFlipVertical ()
134
	{
135
		$x_i = imagesx($this->workingImage);
136
	    $y_i = imagesy($this->workingImage);
137

  
138
	    for ($x = 0; $x < $x_i; $x++) 
139
		{
140
	        for ($y = 0; $y < $y_i; $y++) 
141
			{
142
	            imagecopy($this->workingImage, $this->workingImage, $x, $y_i - $y - 1, $x, $y, 1, 1);
143
	        }
144
	    }
145
	}
146
	
147
	/**
148
	 * Converts a hex color to rgb tuples
149
	 * 
150
	 * @return mixed 
151
	 * @param string $hex
152
	 * @param bool $asString
153
	 */
154
	protected function hex2rgb ($hex, $asString = false) 
155
	{
156
        // strip off any leading #
157
        if (0 === strpos($hex, '#')) 
158
		{
159
           $hex = substr($hex, 1);
160
        } 
161
		elseif (0 === strpos($hex, '&H')) 
162
		{
163
           $hex = substr($hex, 2);
164
        }
165

  
166
        // break into hex 3-tuple
167
        $cutpoint = ceil(strlen($hex) / 2)-1;
168
        $rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3);
169

  
170
        // convert each tuple to decimal
171
        $rgb[0] = (isset($rgb[0]) ? hexdec($rgb[0]) : 0);
172
        $rgb[1] = (isset($rgb[1]) ? hexdec($rgb[1]) : 0);
173
        $rgb[2] = (isset($rgb[2]) ? hexdec($rgb[2]) : 0);
174

  
175
        return ($asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb);
176
    }
177
}
178

  
179
$pt = PhpThumb::getInstance();
180
$pt->registerPlugin('GdReflectionLib', 'gd');
0 181

  
branches/2.8.x/wb/admin/media/inc/ThumbLib.inc.php
1
<?php
2
/**
3
 * PhpThumb Library Definition File
4
 * 
5
 * This file contains the definitions for the PhpThumbFactory class.
6
 * It also includes the other required base class files.
7
 * 
8
 * If you've got some auto-loading magic going on elsewhere in your code, feel free to
9
 * remove the include_once statements at the beginning of this file... just make sure that
10
 * these files get included one way or another in your code.
11
 * 
12
 * PHP Version 5 with GD 2.0+
13
 * PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
14
 * Copyright (c) 2009, Ian Selby/Gen X Design
15
 * 
16
 * Author(s): Ian Selby <ian@gen-x-design.com>
17
 * 
18
 * Licensed under the MIT License
19
 * Redistributions of files must retain the above copyright notice.
20
 * 
21
 * @author Ian Selby <ian@gen-x-design.com>
22
 * @copyright Copyright (c) 2009 Gen X Design
23
 * @link http://phpthumb.gxdlabs.com
24
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
25
 * @version 3.0
26
 * @package PhpThumb
27
 * @filesource
28
 */
29

  
30
// define some useful constants
31
define('THUMBLIB_BASE_PATH', str_replace('\\','/',dirname(__FILE__)) );
32
define('THUMBLIB_PLUGIN_PATH', THUMBLIB_BASE_PATH . '/thumb_plugins/');
33
define('DEFAULT_THUMBLIB_IMPLEMENTATION', 'gd');
34

  
35
/**
36
 * Include the PhpThumb Class
37
 */
38
require_once THUMBLIB_BASE_PATH . '/PhpThumb.inc.php';
39
/**
40
 * Include the ThumbBase Class
41
 */
42
require_once THUMBLIB_BASE_PATH . '/ThumbBase.inc.php';
43
/**
44
 * Include the GdThumb Class
45
 */
46
require_once THUMBLIB_BASE_PATH . '/GdThumb.inc.php';
47

  
48
/**
49
 * PhpThumbFactory Object
50
 * 
51
 * This class is responsible for making sure everything is set up and initialized properly,
52
 * and returning the appropriate thumbnail class instance.  It is the only recommended way 
53
 * of using this library, and if you try and circumvent it, the sky will fall on your head :)
54
 * 
55
 * Basic use is easy enough.  First, make sure all the settings meet your needs and environment...
56
 * these are the static variables defined at the beginning of the class.
57
 * 
58
 * Once that's all set, usage is pretty easy.  You can simply do something like:
59
 * <code>$thumb = PhpThumbFactory::create('/path/to/file.png');</code>
60
 * 
61
 * Refer to the documentation for the create function for more information
62
 * 
63
 * @package PhpThumb
64
 * @subpackage Core
65
 */
66
class PhpThumbFactory
67
{
68
	/**
69
	 * Which implemenation of the class should be used by default
70
	 * 
71
	 * Currently, valid options are:
72
	 *  - imagick
73
	 *  - gd
74
	 *  
75
	 * These are defined in the implementation map variable, inside the create function
76
	 * 
77
	 * @var string
78
	 */
79
	public static $defaultImplemenation = DEFAULT_THUMBLIB_IMPLEMENTATION;
80
	/**
81
	 * Where the plugins can be loaded from
82
	 * 
83
	 * Note, it's important that this path is properly defined.  It is very likely that you'll 
84
	 * have to change this, as the assumption here is based on a relative path.
85
	 * 
86
	 * @var string
87
	 */
88
	public static $pluginPath = THUMBLIB_PLUGIN_PATH;
89
	
90
	/**
91
	 * Factory Function
92
	 * 
93
	 * This function returns the correct thumbnail object, augmented with any appropriate plugins.  
94
	 * It does so by doing the following:
95
	 *  - Getting an instance of PhpThumb
96
	 *  - Loading plugins
97
	 *  - Validating the default implemenation
98
	 *  - Returning the desired default implementation if possible
99
	 *  - Returning the GD implemenation if the default isn't available
100
	 *  - Throwing an exception if no required libraries are present
101
	 * 
102
	 * @return GdThumb
103
	 * @uses PhpThumb
104
	 * @param string $filename The path and file to load [optional]
105
	 */
106
	public static function create ($filename = null, $options = array(), $isDataStream = false)
107
	{
108
		// map our implementation to their class names
109
		$implementationMap = array
110
		(
111
			'imagick'	=> 'ImagickThumb',
112
			'gd' 		=> 'GdThumb'
113
		);
114
		
115
		// grab an instance of PhpThumb
116
		$pt = PhpThumb::getInstance();
117
		// load the plugins
118
		$pt->loadPlugins(self::$pluginPath);
119
		
120
		$toReturn = null;
121
		$implementation = self::$defaultImplemenation;
122
		
123
		// attempt to load the default implementation
124
		if ($pt->isValidImplementation(self::$defaultImplemenation))
125
		{
126
			$imp = $implementationMap[self::$defaultImplemenation];
127
			$toReturn = new $imp($filename, $options, $isDataStream);
128
		}
129
		// load the gd implementation if default failed
130
		else if ($pt->isValidImplementation('gd'))
131
		{
132
			$imp = $implementationMap['gd'];
133
			$implementation = 'gd';
134
			$toReturn = new $imp($filename, $options, $isDataStream);
135
		}
136
		// throw an exception if we can't load
137
		else
138
		{
139
			throw new Exception('You must have either the GD or iMagick extension loaded to use this library');
140
		}
141
		
142
		$registry = $pt->getPluginRegistry($implementation);
143
		$toReturn->importPlugins($registry);
144
		return $toReturn;
145
	}
146
}
0 147

  
branches/2.8.x/wb/admin/media/inc/PhpThumb.inc.php
1
<?php
2
/**
3
 * PhpThumb Library Definition File
4
 * 
5
 * This file contains the definitions for the PhpThumb class.
6
 * 
7
 * PHP Version 5 with GD 2.0+
8
 * PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
9
 * Copyright (c) 2009, Ian Selby/Gen X Design
10
 * 
11
 * Author(s): Ian Selby <ian@gen-x-design.com>
12
 * 
13
 * Licensed under the MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 * 
16
 * @author Ian Selby <ian@gen-x-design.com>
17
 * @copyright Copyright (c) 2009 Gen X Design
18
 * @link http://phpthumb.gxdlabs.com
19
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
20
 * @version 3.0
21
 * @package PhpThumb
22
 * @filesource
23
 */
24

  
25

  
26

  
27
/**
28
 * PhpThumb Object
29
 * 
30
 * This singleton object is essentially a function library that helps with core validation 
31
 * and loading of the core classes and plugins.  There isn't really any need to access it directly, 
32
 * unless you're developing a plugin and need to take advantage of any of the functionality contained 
33
 * within.
34
 * 
35
 * If you're not familiar with singleton patterns, here's how you get an instance of this class (since you 
36
 * can't create one via the new keyword):
37
 * <code>$pt = PhpThumb::getInstance();</code>
38
 * 
39
 * It's that simple!  Outside of that, there's no need to modify anything within this class, unless you're doing 
40
 * some crazy customization... then knock yourself out! :)
41
 * 
42
 * @package PhpThumb
43
 * @subpackage Core
44
 */
45
class PhpThumb
46
{
47
	/**
48
	 * Instance of self
49
	 * 
50
	 * @var object PhpThumb
51
	 */
52
	protected static $_instance;
53
	/**
54
	 * The plugin registry
55
	 * 
56
	 * This is where all plugins to be loaded are stored.  Data about the plugin is 
57
	 * provided, and currently consists of:
58
	 *  - loaded: true/false
59
	 *  - implementation: gd/imagick/both
60
	 * 
61
	 * @var array
62
	 */
63
	protected $_registry;
64
	/**
65
	 * What implementations are available
66
	 * 
67
	 * This stores what implementations are available based on the loaded 
68
	 * extensions in PHP, NOT whether or not the class files are present.
69
	 * 
70
	 * @var array
71
	 */
72
	protected $_implementations;
73
	
74
	/**
75
	 * Returns an instance of self
76
	 * 
77
	 * This is the usual singleton function that returns / instantiates the object
78
	 * 
79
	 * @return PhpThumb
80
	 */
81
	public static function getInstance ()
82
	{
83
		if(!(self::$_instance instanceof self))
84
		{
85
			self::$_instance = new self();
86
		}
87

  
88
		return self::$_instance;
89
	}
90
	
91
	/**
92
	 * Class constructor
93
	 * 
94
	 * Initializes all the variables, and does some preliminary validation / checking of stuff
95
	 * 
96
	 */
97
	private function __construct ()
98
	{
99
		$this->_registry		= array();
100
		$this->_implementations	= array('gd' => false, 'imagick' => false);
101
		
102
		$this->getImplementations();
103
	}
104
	
105
	/**
106
	 * Finds out what implementations are available
107
	 * 
108
	 * This function loops over $this->_implementations and validates that the required extensions are loaded.
109
	 * 
110
	 * I had planned on attempting to load them dynamically via dl(), but that would provide more overhead than I 
111
	 * was comfortable with (and would probably fail 99% of the time anyway)
112
	 * 
113
	 */
114
	private function getImplementations ()
115
	{
116
		foreach($this->_implementations as $extension => $loaded)
117
		{
118
			if($loaded)
119
			{
120
				continue;
121
			}
122
			
123
			if(extension_loaded($extension))
124
			{
125
				$this->_implementations[$extension] = true;
126
			}
127
		}
128
	}
129
	
130
	/**
131
	 * Returns whether or not $implementation is valid (available)
132
	 * 
133
	 * If 'all' is passed, true is only returned if ALL implementations are available.
134
	 * 
135
	 * You can also pass 'n/a', which always returns true
136
	 * 
137
	 * @return bool 
138
	 * @param string $implementation
139
	 */
140
	public function isValidImplementation ($implementation)
141
	{
142
		if ($implementation == 'n/a')
143
		{
144
			return true;
145
		}
146
		
147
		if ($implementation == 'all')
148
		{
149
			foreach ($this->_implementations as $imp => $value)
150
			{
151
				if ($value == false)
152
				{
153
					return false;
154
				}
155
			}
156
			
157
			return true;
158
		}
159
		
160
		if (array_key_exists($implementation, $this->_implementations))
161
		{
162
			return $this->_implementations[$implementation];
163
		}
164
		
165
		return false;
166
	}
167
	
168
	/**
169
	 * Registers a plugin in the registry
170
	 * 
171
	 * Adds a plugin to the registry if it isn't already loaded, and if the provided 
172
	 * implementation is valid.  Note that you can pass the following special keywords 
173
	 * for implementation:
174
	 *  - all - Requires that all implementations be available
175
	 *  - n/a - Doesn't require any implementation
176
	 *  
177
	 * When a plugin is added to the registry, it's added as a key on $this->_registry with the value 
178
	 * being an array containing the following keys:
179
	 *  - loaded - whether or not the plugin has been "loaded" into the core class
180
	 *  - implementation - what implementation this plugin is valid for
181
	 * 
182
	 * @return bool
183
	 * @param string $pluginName
184
	 * @param string $implementation
185
	 */
186
	public function registerPlugin ($pluginName, $implementation)
187
	{
188
		if (!array_key_exists($pluginName, $this->_registry) && $this->isValidImplementation($implementation))
189
		{
190
			$this->_registry[$pluginName] = array('loaded' => false, 'implementation' => $implementation);
191
			return true;
192
		}
193
		
194
		return false;
195
	}
196
	
197
	/**
198
	 * Loads all the plugins in $pluginPath
199
	 * 
200
	 * All this function does is include all files inside the $pluginPath directory.  The plugins themselves 
201
	 * will not be added to the registry unless you've properly added the code to do so inside your plugin file.
202
	 * 
203
	 * @param string $pluginPath
204
	 */
205
	public function loadPlugins ($pluginPath)
206
	{
207
		// strip the trailing slash if present
208
		if (substr($pluginPath, strlen($pluginPath) - 1, 1) == '/')
209
		{
210
			$pluginPath = substr($pluginPath, 0, strlen($pluginPath) - 1);
211
		}
212
		
213
		if ($handle = opendir($pluginPath))
214
		{
215
			while (false !== ($file = readdir($handle)))
216
			{
217
				if ($file == '.' || $file == '..' || $file == '.svn')
218
				{
219
					continue;
220
				}
221
				
222
				include_once($pluginPath . '/' . $file);
223
			}
224
		}
225
	}
226
	
227
	/**
228
	 * Returns the plugin registry for the supplied implementation
229
	 * 
230
	 * @return array
231
	 * @param string $implementation
232
	 */
233
	public function getPluginRegistry ($implementation)
234
	{
235
		$returnArray = array();
236
		
237
		foreach ($this->_registry as $plugin => $meta)
238
		{
239
			if ($meta['implementation'] == 'n/a' || $meta['implementation'] == $implementation)
240
			{
241
				$returnArray[$plugin] = $meta;
242
			}
243
		}
244
		
245
		return $returnArray;
246
	}
247
}
0 248

  
branches/2.8.x/wb/admin/media/inc/GdThumb.inc.php
1
<?php
2
/**
3
 * PhpThumb GD Thumb Class Definition File
4
 * 
5
 * This file contains the definition for the GdThumb object
6
 * 
7
 * PHP Version 5 with GD 2.0+
8
 * PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
9
 * Copyright (c) 2009, Ian Selby/Gen X Design
10
 * 
11
 * Author(s): Ian Selby <ian@gen-x-design.com>
12
 * 
13
 * Licensed under the MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 * 
16
 * @author Ian Selby <ian@gen-x-design.com>
17
 * @copyright Copyright (c) 2009 Gen X Design
18
 * @link http://phpthumb.gxdlabs.com
19
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
20
 * @version 3.0
21
 * @package PhpThumb
22
 * @filesource
23
 */
24

  
25
/**
26
 * GdThumb Class Definition
27
 * 
28
 * This is the GD Implementation of the PHP Thumb library.
29
 * 
30
 * @package PhpThumb
31
 * @subpackage Core
32
 */
33
class GdThumb extends ThumbBase
34
{
35
	/**
36
	 * The prior image (before manipulation)
37
	 * 
38
	 * @var resource
39
	 */
40
	protected $oldImage;
41
	/**
42
	 * The working image (used during manipulation)
43
	 * 
44
	 * @var resource
45
	 */
46
	protected $workingImage;
47
	/**
48
	 * The current dimensions of the image
49
	 * 
50
	 * @var array
51
	 */
52
	protected $currentDimensions;
53
	/**
54
	 * The new, calculated dimensions of the image
55
	 * 
56
	 * @var array
57
	 */
58
	protected $newDimensions;
59
	/**
60
	 * The options for this class
61
	 * 
62
	 * This array contains various options that determine the behavior in
63
	 * various functions throughout the class.  Functions note which specific 
64
	 * option key / values are used in their documentation
65
	 * 
66
	 * @var array
67
	 */
68
	protected $options;
69
	/**
70
	 * The maximum width an image can be after resizing (in pixels)
71
	 * 
72
	 * @var int
73
	 */
74
	protected $maxWidth;
75
	/**
76
	 * The maximum height an image can be after resizing (in pixels)
77
	 * 
78
	 * @var int
79
	 */
80
	protected $maxHeight;
81
	/**
82
	 * The percentage to resize the image by
83
	 * 
84
	 * @var int
85
	 */
86
	protected $percent;
87
	
88
	/**
89
	 * Class Constructor
90
	 * 
91
	 * @return GdThumb 
92
	 * @param string $fileName
93
	 */
94
	public function __construct ($fileName, $options = array(), $isDataStream = false)
95
	{
96
		parent::__construct($fileName, $isDataStream);
97
		
98
		$this->determineFormat();
99
		
100
		if ($this->isDataStream === false)
101
		{
102
			$this->verifyFormatCompatiblity();
103
		}
104
		
105
		switch ($this->format)
106
		{
107
			case 'GIF':
108
				$this->oldImage = imagecreatefromgif($this->fileName);
109
				break;
110
			case 'JPG':
111
				$this->oldImage = imagecreatefromjpeg($this->fileName);
112
				break;
113
			case 'PNG':
114
				$this->oldImage = imagecreatefrompng($this->fileName);
115
				break;
116
			case 'STRING':
117
				$this->oldImage = imagecreatefromstring($this->fileName);
118
				break;
119
		}
120
	
121
		$this->currentDimensions = array
122
		(
123
			'width' 	=> imagesx($this->oldImage),
124
			'height'	=> imagesy($this->oldImage)
125
		);
126
		
127
		$this->setOptions($options);
128
		
129
		// TODO: Port gatherImageMeta to a separate function that can be called to extract exif data
130
	}
131
	
132
	/**
133
	 * Class Destructor
134
	 * 
135
	 */
136
	public function __destruct ()
137
	{
138
		if (is_resource($this->oldImage))
139
		{
140
			imagedestroy($this->oldImage);
141
		}
142
		
143
		if (is_resource($this->workingImage))
144
		{
145
			imagedestroy($this->workingImage);
146
		}
147
	}
148
	
149
	##############################
150
	# ----- API FUNCTIONS ------ #
151
	##############################
152
	
153
	/**
154
	 * Resizes an image to be no larger than $maxWidth or $maxHeight
155
	 * 
156
	 * If either param is set to zero, then that dimension will not be considered as a part of the resize.
157
	 * Additionally, if $this->options['resizeUp'] is set to true (false by default), then this function will
158
	 * also scale the image up to the maximum dimensions provided.
159
	 * 
160
	 * @param int $maxWidth The maximum width of the image in pixels
161
	 * @param int $maxHeight The maximum height of the image in pixels
162
	 * @return GdThumb
163
	 */
164
	public function resize ($maxWidth = 0, $maxHeight = 0)
165
	{
166
		// make sure our arguments are valid
167
		if (!is_numeric($maxWidth))
168
		{
169
			throw new InvalidArgumentException('$maxWidth must be numeric');
170
		}
171
		
172
		if (!is_numeric($maxHeight))
173
		{
174
			throw new InvalidArgumentException('$maxHeight must be numeric');
175
		}
176
		
177
		// make sure we're not exceeding our image size if we're not supposed to
178
		if ($this->options['resizeUp'] === false)
179
		{
180
			$this->maxHeight	= (intval($maxHeight) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $maxHeight;
181
			$this->maxWidth		= (intval($maxWidth) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $maxWidth;
182
		}
183
		else
184
		{
185
			$this->maxHeight	= intval($maxHeight);
186
			$this->maxWidth		= intval($maxWidth);
187
		}
188
		
189
		// get the new dimensions...
190
		$this->calcImageSize($this->currentDimensions['width'], $this->currentDimensions['height']);
191
		
192
		// create the working image
193
		if (function_exists('imagecreatetruecolor'))
194
		{
195
			$this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
196
		}
197
		else
198
		{
199
			$this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
200
		}
201
		
202
		$this->preserveAlpha();		
203
		
204
		// and create the newly sized image
205
		imagecopyresampled
206
		(
207
			$this->workingImage,
208
			$this->oldImage,
209
			0,
210
			0,
211
			0,
212
			0,
213
			$this->newDimensions['newWidth'],
214
			$this->newDimensions['newHeight'],
215
			$this->currentDimensions['width'],
216
			$this->currentDimensions['height']
217
		);
218

  
219
		// update all the variables and resources to be correct
220
		$this->oldImage 					= $this->workingImage;
221
		$this->currentDimensions['width'] 	= $this->newDimensions['newWidth'];
222
		$this->currentDimensions['height'] 	= $this->newDimensions['newHeight'];
223
		
224
		return $this;
225
	}
226
	
227
	/**
228
	 * Adaptively Resizes the Image
229
	 * 
230
	 * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the 
231
	 * remaining overflow (from the center) to get the image to be the size specified
232
	 * 
233
	 * @param int $maxWidth
234
	 * @param int $maxHeight
235
	 * @return GdThumb
236
	 */
237
	public function adaptiveResize ($width, $height)
238
	{
239
		// make sure our arguments are valid
240
		if (!is_numeric($width) || $width  == 0)
241
		{
242
			throw new InvalidArgumentException('$width must be numeric and greater than zero');
243
		}
244
		
245
		if (!is_numeric($height) || $height == 0)
246
		{
247
			throw new InvalidArgumentException('$height must be numeric and greater than zero');
248
		}
249
		
250
		// make sure we're not exceeding our image size if we're not supposed to
251
		if ($this->options['resizeUp'] === false)
252
		{
253
			$this->maxHeight	= (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
254
			$this->maxWidth		= (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
255
		}
256
		else
257
		{
258
			$this->maxHeight	= intval($height);
259
			$this->maxWidth		= intval($width);
260
		}
261
		
262
		$this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']);
263
		
264
		// resize the image to be close to our desired dimensions
265
		$this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
266
		
267
		// reset the max dimensions...
268
		if ($this->options['resizeUp'] === false)
269
		{
270
			$this->maxHeight	= (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
271
			$this->maxWidth		= (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
272
		}
273
		else
274
		{
275
			$this->maxHeight	= intval($height);
276
			$this->maxWidth		= intval($width);
277
		}
278
		
279
		// create the working image
280
		if (function_exists('imagecreatetruecolor'))
281
		{
282
			$this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight);
283
		}
284
		else
285
		{
286
			$this->workingImage = imagecreate($this->maxWidth, $this->maxHeight);
287
		}
288
		
289
		$this->preserveAlpha();
290
		
291
		$cropWidth	= $this->maxWidth;
292
		$cropHeight	= $this->maxHeight;
293
		$cropX 		= 0;
294
		$cropY 		= 0;
295
		
296
		// now, figure out how to crop the rest of the image...
297
		if ($this->currentDimensions['width'] > $this->maxWidth)
298
		{
299
			$cropX = intval(($this->currentDimensions['width'] - $this->maxWidth) / 2);
300
		}
301
		elseif ($this->currentDimensions['height'] > $this->maxHeight)
302
		{
303
			$cropY = intval(($this->currentDimensions['height'] - $this->maxHeight) / 2);
304
		}
305
		
306
		imagecopyresampled
307
		(
308
            $this->workingImage,
309
            $this->oldImage,
310
            0,
311
            0,
312
            $cropX,
313
            $cropY,
314
            $cropWidth,
315
            $cropHeight,
316
            $cropWidth,
317
            $cropHeight
318
		);
319
		
320
		// update all the variables and resources to be correct
321
		$this->oldImage 					= $this->workingImage;
322
		$this->currentDimensions['width'] 	= $this->maxWidth;
323
		$this->currentDimensions['height'] 	= $this->maxHeight;
324
		
325
		return $this;
326
	}
327
	
328
	/**
329
	 * Resizes an image by a given percent uniformly
330
	 * 
331
	 * Percentage should be whole number representation (i.e. 1-100)
332
	 * 
333
	 * @param int $percent
334
	 * @return GdThumb
335
	 */
336
	public function resizePercent ($percent = 0)
337
	{
338
		if (!is_numeric($percent))
339
		{
340
			throw new InvalidArgumentException ('$percent must be numeric');
341
		}
342
		
343
		$this->percent = intval($percent);
344
		
345
		$this->calcImageSizePercent($this->currentDimensions['width'], $this->currentDimensions['height']);
346
		
347
		if (function_exists('imagecreatetruecolor'))
348
		{
349
			$this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
350
		}
351
		else
352
		{
353
			$this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
354
		}
355
		
356
		$this->preserveAlpha();
357
		
358
		ImageCopyResampled(
359
			$this->workingImage,
360
			$this->oldImage,
361
			0,
362
			0,
363
			0,
364
			0,
365
			$this->newDimensions['newWidth'],
366
			$this->newDimensions['newHeight'],
367
			$this->currentDimensions['width'],
368
			$this->currentDimensions['height']
369
		);
370

  
371
		$this->oldImage 					= $this->workingImage;
372
		$this->currentDimensions['width'] 	= $this->newDimensions['newWidth'];
373
		$this->currentDimensions['height'] 	= $this->newDimensions['newHeight'];
374
		
375
		return $this;
376
	}
377
	
378
	/**
379
	 * Crops an image from the center with provided dimensions
380
	 * 
381
	 * If no height is given, the width will be used as a height, thus creating a square crop
382
	 * 
383
	 * @param int $cropWidth
384
	 * @param int $cropHeight
385
	 * @return GdThumb
386
	 */
387
	public function cropFromCenter ($cropWidth, $cropHeight = null)
388
	{
389
		if (!is_numeric($cropWidth))
390
		{
391
			throw new InvalidArgumentException('$cropWidth must be numeric');
392
		}
393
		
394
		if ($cropHeight !== null && !is_numeric($cropHeight))
395
		{
396
			throw new InvalidArgumentException('$cropHeight must be numeric');
397
		}
398
		
399
		if ($cropHeight === null)
400
		{
401
			$cropHeight = $cropWidth;
402
		}
403
		
404
		$cropWidth	= ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth;
405
		$cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight;
406
		
407
		$cropX = intval(($this->currentDimensions['width'] - $cropWidth) / 2);
408
		$cropY = intval(($this->currentDimensions['height'] - $cropHeight) / 2);
409
		
410
		$this->crop($cropX, $cropY, $cropWidth, $cropHeight);
411
		
412
		return $this;
413
	}
414
	
415
	/**
416
	 * Vanilla Cropping - Crops from x,y with specified width and height
417
	 * 
418
	 * @param int $startX
419
	 * @param int $startY
420
	 * @param int $cropWidth
421
	 * @param int $cropHeight
422
	 * @return GdThumb
423
	 */
424
	public function crop ($startX, $startY, $cropWidth, $cropHeight)
425
	{
426
		// validate input
427
		if (!is_numeric($startX))
428
		{
429
			throw new InvalidArgumentException('$startX must be numeric');
430
		}
431
		
432
		if (!is_numeric($startY))
433
		{
434
			throw new InvalidArgumentException('$startY must be numeric');
435
		}
436
		
437
		if (!is_numeric($cropWidth))
438
		{
439
			throw new InvalidArgumentException('$cropWidth must be numeric');
440
		}
441
		
442
		if (!is_numeric($cropHeight))
443
		{
444
			throw new InvalidArgumentException('$cropHeight must be numeric');
445
		}
446
		
447
		// do some calculations
448
		$cropWidth	= ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth;
449
		$cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight;
450
		
451
		// ensure everything's in bounds
452
		if (($startX + $cropWidth) > $this->currentDimensions['width'])
453
		{
454
			$startX = ($this->currentDimensions['width'] - $cropWidth);
455
			
456
		}
457
		
458
		if (($startY + $cropHeight) > $this->currentDimensions['height'])
459
		{
460
			$startY = ($this->currentDimensions['height'] - $cropHeight);
461
		}
462
		
463
		if ($startX < 0) 
464
		{
465
			$startX = 0;
466
		}
467
		
468
	    if ($startY < 0) 
469
		{
470
			$startY = 0;
471
		}
472
		
473
		// create the working image
474
		if (function_exists('imagecreatetruecolor'))
475
		{
476
			$this->workingImage = imagecreatetruecolor($cropWidth, $cropHeight);
477
		}
478
		else
479
		{
480
			$this->workingImage = imagecreate($cropWidth, $cropHeight);
481
		}
482
		
483
		$this->preserveAlpha();
484
		
485
		imagecopyresampled
486
		(
487
			$this->workingImage,
488
			$this->oldImage,
489
			0,
490
			0,
491
			$startX,
492
			$startY,
493
			$cropWidth,
494
			$cropHeight,
495
			$cropWidth,
496
			$cropHeight
497
		);
498
		
499
		$this->oldImage 					= $this->workingImage;
500
		$this->currentDimensions['width'] 	= $cropWidth;
501
		$this->currentDimensions['height'] 	= $cropHeight;
502
		
503
		return $this;
504
	}
505
	
506
	/**
507
	 * Rotates image either 90 degrees clockwise or counter-clockwise
508
	 * 
509
	 * @param string $direction
510
	 * @retunrn GdThumb
511
	 */
512
	public function rotateImage ($direction = 'CW') 
513
	{
514
    	if ($direction == 'CW') 
515
		{
516
    		$this->rotateImageNDegrees(90);
517
    	}
518
    	else 
519
		{
520
			$this->rotateImageNDegrees(-90);
521
		}
522
		
523
		return $this;
524
    }
525
	
526
	/**
527
	 * Rotates image specified number of degrees
528
	 * 
529
	 * @param int $degrees
530
	 * @return GdThumb
531
	 */
532
	public function rotateImageNDegrees ($degrees)
533
	{
534
		if (!is_numeric($degrees))
535
		{
536
			throw new InvalidArgumentException('$degrees must be numeric');
537
		}
538
		
539
		if (!function_exists('imagerotate'))
540
		{
541
			throw new RuntimeException('Your version of GD does not support image rotation.');
542
		}
543
		
544
		$this->workingImage = imagerotate($this->oldImage, $degrees, 0);
545
    	
546
		$newWidth 							= $this->currentDimensions['height'];
547
    	$newHeight 							= $this->currentDimensions['width'];
548
		$this->oldImage 					= $this->workingImage;
549
		$this->currentDimensions['width'] 	= $newWidth;
550
		$this->currentDimensions['height'] 	= $newHeight;
551
		
552
		return $this;
553
	}
554
	
555
	/**
556
	 * Shows an image
557
	 * 
558
	 * This function will show the current image by first sending the appropriate header
559
	 * for the format, and then outputting the image data. If headers have already been sent, 
560
	 * a runtime exception will be thrown 
561
	 * 
562
	 * @param bool $rawData Whether or not the raw image stream should be output
563
	 * @return GdThumb
564
	 */
565
	public function show ($rawData = false) 
566
	{
567
		if (headers_sent())
568
		{
569
			throw new RuntimeException('Cannot show image, headers have already been sent');
570
		}
571
		
572
		switch ($this->format) 
573
		{
574
			case 'GIF':
575
				if ($rawData === false) 
576
				{ 
577
					header('Content-type: image/gif'); 
578
				}
579
				imagegif($this->oldImage);
580
				break;
581
			case 'JPG':
582
				if ($rawData === false) 
583
				{ 
584
					header('Content-type: image/jpeg'); 
585
				}
586
				imagejpeg($this->oldImage, null, $this->options['jpegQuality']);
587
				break;
588
			case 'PNG':
589
			case 'STRING':
590
				if ($rawData === false) 
591
				{ 
592
					header('Content-type: image/png'); 
593
				}
594
				imagepng($this->oldImage);
595
				break;
596
		}
597
		
598
		return $this;
599
	}
600
	
601
	/**
602
	 * Returns the Working Image as a String
603
	 *
604
	 * This function is useful for getting the raw image data as a string for storage in
605
	 * a database, or other similar things.
606
	 *
607
	 * @return string
608
	 */
609
	public function getImageAsString ()
610
	{
611
		$data = null;
612
		ob_start();
613
		$this->show(true);
614
		$data = ob_get_contents();
615
		ob_end_clean();
616
		
617
		return $data;
618
	}
619
	
620
	/**
621
	 * Saves an image
622
	 * 
623
	 * This function will make sure the target directory is writeable, and then save the image.
624
	 * 
625
	 * If the target directory is not writeable, the function will try to correct the permissions (if allowed, this
626
	 * is set as an option ($this->options['correctPermissions']).  If the target cannot be made writeable, then a
627
	 * RuntimeException is thrown.
628
	 * 
629
	 * TODO: Create additional paramter for color matte when saving images with alpha to non-alpha formats (i.e. PNG => JPG)
630
	 * 
631
	 * @param string $fileName The full path and filename of the image to save
632
	 * @param string $format The format to save the image in (optional, must be one of [GIF,JPG,PNG]
633
	 * @return GdThumb
634
	 */
635
	public function save ($fileName, $format = null)
636
	{
637
		$validFormats = array('GIF', 'JPG', 'PNG');
638
		$format = ($format !== null) ? strtoupper($format) : $this->format;
639

  
640
		if (!in_array($format, $validFormats))
641
		{
642
			throw new InvalidArgumentException ('Invalid format type specified in save function: ' . $format);
643
		}
644
		
645
		// make sure the directory is writeable
646
		if (!is_writeable(dirname($fileName)))
647
		{
648
			// try to correct the permissions
649
			if ($this->options['correctPermissions'] === true)
650
			{
651
				@chmod(dirname($fileName), 0777);
652
				
653
				// throw an exception if not writeable
654
				if (!is_writeable(dirname($fileName)))
655
				{
656
					throw new RuntimeException ('File is not writeable, and could not correct permissions: ' . $fileName);
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff