support GD and Imagick

This commit is contained in:
Roland Gruber 2018-08-25 19:39:47 +02:00
parent 2c158e6844
commit 4b0411d448
17 changed files with 600 additions and 372 deletions

View File

@ -8,9 +8,10 @@ Homepage: https://www.ldap-account-manager.org/
Package: ldap-account-manager
Architecture: all
Depends: php5 (>= 5.4.26) | php (>= 7), php5-ldap | php-ldap, php5-gd | php-gd,
php5-json | php-json, php5-imagick | php-imagick, php5-curl | php-curl,
php5 | php-zip, php5 | php-xml, php5-imagick | php-imagick,
Depends: php5 (>= 5.4.26) | php (>= 7), php5-ldap | php-ldap,
php5-gd | php-gd | php5-imagick | php-imagick,
php5-json | php-json, php5-curl | php-curl,
php5 | php-zip, php5 | php-xml,
libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm,
php-tcpdf, php-phpseclib (>= 2.0),
apache2 (>= 2.4.0) | httpd, fonts-dejavu, debconf (>= 0.2.26) | debconf-2.0, ${misc:Depends}

View File

@ -1,7 +1,8 @@
September 2018 6.5
- Password change possible via LDAP EXOP operation (set LDAP_EXOP as password hash)
- Dropped suppurt for Apache 2.2
- Password change possible via LDAP EXOP operation (set LDAP_EXOP as password hash, requires PHP 7.2)
- Dropped support for Apache 2.2
- Upload: allow to overwrite existing accounts
- Personal: photos can be printed in PDF export
- Kolab updates
- LAM Pro:
-> Auto deletion of entries with dynamic directory services support (requires PHP 7.2).

View File

@ -4,17 +4,17 @@
<chapter>
<title>Tools</title>
<para></para>
<para/>
<section id="a_accountProfile">
<title>Profile editor</title>
<para>The account profiles are templates for your accounts. Here you can
specify default values which can then be loaded when you create
accounts. You may also load a template for an existing account to reset
it to default values. When you create a new account then LAM will always
load the profile named <emphasis role="bold">"default"</emphasis>. This
account profile can include default values for all your accounts.</para>
specify default values which can then be loaded when you create accounts.
You may also load a template for an existing account to reset it to
default values. When you create a new account then LAM will always load
the profile named <emphasis role="bold">"default"</emphasis>. This account
profile can include default values for all your accounts.</para>
<screenshot>
<mediaobject>
@ -57,13 +57,13 @@
</screenshot>
<para>There is a special export target called "*Global templates". All
profiles exported here will be copied to all other server profiles
(incl. new ones). But existing profiles with the same name are not
overwritten. So a profile in global templates is treated as default
profile for all server profiles.</para>
profiles exported here will be copied to all other server profiles (incl.
new ones). But existing profiles with the same name are not overwritten.
So a profile in global templates is treated as default profile for all
server profiles.</para>
<para>Use this if you would like to setup default profiles that are
valid for all server profiles.</para>
<para>Use this if you would like to setup default profiles that are valid
for all server profiles.</para>
<screenshot>
<mediaobject>
@ -77,14 +77,14 @@
<section>
<title>File upload</title>
<para>When you need to create lots of accounts then you can use LAM's
file upload to create them. LAM will read a CSV formatted file and
create the related LDAP entries. Please check the data in you CSV file
carefully. LAM will do less checks for the file upload than for single
account creation.</para>
<para>When you need to create lots of accounts then you can use LAM's file
upload to create them. LAM will read a CSV formatted file and create the
related LDAP entries. Please check the data in you CSV file carefully. LAM
will do less checks for the file upload than for single account
creation.</para>
<para>At the first page please select the account type and what
extensions should be activated.</para>
<para>At the first page please select the account type and what extensions
should be activated.</para>
<screenshot>
<mediaobject>
@ -99,13 +99,13 @@
CSV file. All red options are required columns in the file. You need to
specify a value for each account.</para>
<para>When you upload the CSV file then LAM first does some checks on
this file. This includes syntax checks and if all required data was
entered. No changes in the LDAP directory are done at this time.</para>
<para>When you upload the CSV file then LAM first does some checks on this
file. This includes syntax checks and if all required data was entered. No
changes in the LDAP directory are done at this time.</para>
<para>If the checks were successful then LAM will ask again if you want
to create the accounts. You will also have the chance to check the
upload by viewing the changes in LDIF format.</para>
<para>If the checks were successful then LAM will ask again if you want to
create the accounts. You will also have the chance to check the upload by
viewing the changes in LDIF format.</para>
<screenshot>
<mediaobject>
@ -119,20 +119,19 @@
<section>
<title id="toolMultiEdit">Multi edit</title>
<para>This tool allows you to modify a large list of LDAP entries in
batch mode. You can add new attributes/object classes, remove attributes
and set attributes to a specific value.</para>
<para>This tool allows you to modify a large list of LDAP entries in batch
mode. You can add new attributes/object classes, remove attributes and set
attributes to a specific value.</para>
<para>At the beginning, you need to specify where the entries are stored
that should be changed. You can select an account suffix, the tree
suffix or enter your own DN by selecting "Other".</para>
that should be changed. You can select an account suffix, the tree suffix
or enter your own DN by selecting "Other".</para>
<para>Next, enter an additional LDAP filter to limit the entries that
should be changed. E.g. use "(objectclass=inetOrgPerson)" to filter for
users. You may also enter e.g. "(!(objectClass=passwordSelfReset))" to
match all accounts that do not yet have the <link
linkend="passwordSelfResetUser">password self reset</link>
feature.</para>
linkend="passwordSelfResetUser">password self reset</link> feature.</para>
<literallayout>
</literallayout>
@ -142,9 +141,8 @@
<itemizedlist>
<listitem>
<para>Add: Adds an attribute value if not yet existing. Please do
not use for single-value attributes that already have a
value.</para>
<para>Add: Adds an attribute value if not yet existing. Please do not
use for single-value attributes that already have a value.</para>
</listitem>
<listitem>
@ -154,14 +152,14 @@
</listitem>
<listitem>
<para>Delete: Deletes the specified value from this attribute. If
you leave the value field blank then all attribute values are
<para>Delete: Deletes the specified value from this attribute. If you
leave the value field blank then all attribute values are
removed.</para>
</listitem>
</itemizedlist>
<para>Please note that all actions are run as separate LDAP commands.
You cannot add an object class and a required attribute at the same
<para>Please note that all actions are run as separate LDAP commands. You
cannot add an object class and a required attribute at the same
time.</para>
<screenshot>
@ -175,9 +173,9 @@
<para><emphasis role="bold">Dry run</emphasis></para>
<para>You should always start with a dry run. It will not do any changes
to your LDAP directory but print out all modifications that will be
done. You will also be able to download the changes in LDIF format to
use with ldapmodify. This is useful if you want to adjust some actions
to your LDAP directory but print out all modifications that will be done.
You will also be able to download the changes in LDIF format to use with
ldapmodify. This is useful if you want to adjust some actions
manually.</para>
<screenshot>
@ -190,9 +188,9 @@
<para><emphasis role="bold">Apply changes</emphasis></para>
<para>This will run the actions against your LDAP directory. You will
see which accounts are edited in the progress area and also if any
errors occured.</para>
<para>This will run the actions against your LDAP directory. You will see
which accounts are edited in the progress area and also if any errors
occured.</para>
<screenshot>
<mediaobject>
@ -234,9 +232,9 @@
</screenshot>
<para>When you export accounts to PDF then each account will get its own
page inside the PDF. There is a headline on each page where you can show
a page title. You may also add a logo to each page. To add more logos
please use the logo management on the PDF editor main page.</para>
page inside the PDF. There is a headline on each page where you can show a
page title. You may also add a logo to each page. To add more logos please
use the logo management on the PDF editor main page.</para>
<screenshot>
<mediaobject>
@ -252,8 +250,8 @@
can be moved by using the arrows next to the section title.</para>
<para>Each section can contain multiple fields which usually represent
LDAP attributes. You can simply add new fields by selecting the field
name and its position. Then use the arrows to move the field inside the
LDAP attributes. You can simply add new fields by selecting the field name
and its position. Then use the arrows to move the field inside the
section.</para>
<literallayout>
@ -280,14 +278,14 @@
</mediaobject>
</screenshot>
<para>There is a special export target called "*Global templates". All
PDF structures exported here will be copied to all other server profiles
<para>There is a special export target called "*Global templates". All PDF
structures exported here will be copied to all other server profiles
(incl. new ones). But existing PDF structures with the same name are not
overwritten. So a PDF structure in global templates is treated as
default structure for all server profiles.</para>
overwritten. So a PDF structure in global templates is treated as default
structure for all server profiles.</para>
<para>Use this if you would like to setup default PDF structures that
are valid for all server profiles.</para>
<para>Use this if you would like to setup default PDF structures that are
valid for all server profiles.</para>
<screenshot>
<mediaobject>
@ -300,8 +298,7 @@
<para><emphasis role="bold">Logo management:</emphasis></para>
<para>You can upload image files to put a custom logo on the PDF files.
The image file name must end with .png or .jpg and the size must not
exceed 2000x300px.</para>
The image file name must end with .png or .jpg.</para>
<screenshot>
<mediaobject>
@ -334,8 +331,8 @@
<para>This shows information and statistics about your LDAP server. This
includes the suffixes, used overlays, connection data and operation
statistics. You will need "cn=monitor" setup to see all details. Some
data may not be available depending on your LDAP server software.</para>
statistics. You will need "cn=monitor" setup to see all details. Some data
may not be available depending on your LDAP server software.</para>
<para>Please see the following links how to setup "cn=monitor":</para>
@ -364,8 +361,8 @@
<section>
<title>Tests</title>
<para>This allows you to check if your LDAP schema is compatible with
LAM and to find possible problems.</para>
<para>This allows you to check if your LDAP schema is compatible with LAM
and to find possible problems.</para>
<section>
<title>Lamdaemon test</title>
@ -390,13 +387,13 @@
<section>
<title>Schema test</title>
<para>This will test if your LDAP schema supports all object classes
and attributes of the active LAM modules. If you get a message that
<para>This will test if your LDAP schema supports all object classes and
attributes of the active LAM modules. If you get a message that
something is missing please check that you installed all <link
linkend="a_schema">required schemas</link>.</para>
<para>If you get error messages about object class violations then
this test can tell you what is missing.</para>
<para>If you get error messages about object class violations then this
test can tell you what is missing.</para>
<screenshot>
<mediaobject>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -3,6 +3,7 @@ use \LAM\PDF\PDFLabelValue;
use \LAM\PDF\PDFTable;
use LAM\TYPES\ConfiguredType;
use function LAM\TYPES\getScopeFromTypeId;
use LAM\PDF\PDFImage;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
@ -846,6 +847,19 @@ abstract class baseModule {
$result[get_class($this) . '_' . $name][] = $table;
}
/**
* Adds an image to the PDF.
*
* @param array $result result array (entry will be added here)
* @param String $attrName attribute name
* @param PDFTable $table table
*/
public function addPDFImage(&$result, $attrName) {
if (isset($this->attributes[$attrName]) && (sizeof($this->attributes[$attrName]) > 0)) {
$result[get_class($this) . '_' . $attrName][] = new PDFImage($this->attributes[$attrName][0]);
}
}
/**
* Returns an array containing all input columns for the file upload.
*

View File

@ -81,13 +81,9 @@ if (! function_exists('utf8_decode') || !extension_loaded('xml')) {
if (!extension_loaded('libxml')) {
$criticalErrors[] = array("ERROR", "Your PHP has no Lib XML support!", "Please install the Lib XML extension for PHP.");
}
// imagick
if (!extension_loaded('imagick')) {
$criticalErrors[] = array("ERROR", "Your PHP has no imagick support.", "Please install the imagick extension for PHP.");
}
// check if PHP has GD support
if (! function_exists('getimagesize')) {
$criticalErrors[] = array("ERROR", "Your PHP has no GD support!", "Please install the GD extension for PHP.");
// imagick/GD
if (!extension_loaded('imagick') && !function_exists('getimagesize')) {
$criticalErrors[] = array("ERROR", "Your PHP has no imagick or GD support.", "Please install the imagick or GD extension for PHP.");
}
// check if PHP has JSON support
if (! function_exists('json_encode')) {

View File

@ -44,6 +44,20 @@ class ImageManipulationFactory {
if (extension_loaded('imagick')) {
return new ImageManipulatorImagick($imageData);
}
return new ImageManipulatorGd($imageData);
}
/**
* Returns an image manipulator based on installed PHP modules.
*
* @param string $path path to image file
* @return ImageManipulator manipulator
*/
public static function getImageManipulatorFromFile($path) {
$handle = fopen($path, "r");
$data = fread($handle, 100000000);
fclose($handle);
return ImageManipulationFactory::getImageManipulator($data);
}
}
@ -130,7 +144,7 @@ class ImageManipulatorImagick implements ImageManipulator {
* @see \LAM\ImageUtils\ImageManipulator::getHeight()
*/
public function getHeight() {
$this->image->getimageheight();
return $this->image->getimageheight();
}
/**
@ -138,7 +152,7 @@ class ImageManipulatorImagick implements ImageManipulator {
* @see \LAM\ImageUtils\ImageManipulator::getWidth()
*/
public function getWidth() {
$this->image->getimagewidth();
return $this->image->getimagewidth();
}
/**
@ -157,15 +171,19 @@ class ImageManipulatorImagick implements ImageManipulator {
public function crop($x, $y, $width, $height) {
$this->image->cropimage($width, $height, $x, $y);
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::resize()
*/
public function thumbnail($width, $height) {
if (($this->getWidth() <= $width) && ($this->getHeight() <= $height)) {
// skip if smaller than target size
return;
}
$this->image->thumbnailimage($width, $height, true);
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::getImageData()
@ -176,5 +194,134 @@ class ImageManipulatorImagick implements ImageManipulator {
}
/**
* Manipulates images using gd library.
*
* @author Roland Gruber
*/
class ImageManipulatorGd implements ImageManipulator {
/**
* Image
*
* @var resource image
*/
private $image;
/**
* GD image type
*
* @var int image type
*/
private $type;
/**
* Constructor.
*
* @param string $imageData original image as binary string
*/
public function __construct($imageData) {
$this->image = imagecreatefromstring($imageData);
$info = getimagesizefromstring($imageData);
$this->type = $info[2];
}
/**
* Destructor
*/
public function __destruct() {
if ($this->image != null) {
imagedestroy($this->image);
}
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::getHeight()
*/
public function getHeight() {
return imagesy($this->image);
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::getWidth()
*/
public function getWidth() {
return imagesx($this->image);
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::getAsJpeg()
*/
public function convertToJpeg() {
$this->type = IMAGETYPE_JPEG;
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::crop()
*/
public function crop($x, $y, $width, $height) {
$this->image = imagecrop($this->image, array(
'x' => $x,
'y' => $y,
'width' => $width,
'height' => $height
));
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::resize()
*/
public function thumbnail($width, $height) {
if (($this->getWidth() <= $width) && ($this->getHeight() <= $height)) {
// skip if smaller than target size
return;
}
$thumbWidth = $this->getWidth();
$thumbHeight = $this->getHeight();
if ($thumbWidth > $width) {
$factor = $width / $thumbWidth;
$thumbWidth = $thumbWidth * $factor;
$thumbHeight = $thumbHeight * $factor;
}
if ($thumbHeight > $height) {
$factor = $height / $thumbHeight;
$thumbWidth = $thumbWidth * $factor;
$thumbHeight = $thumbHeight * $factor;
}
$thumbnail = imagecreatetruecolor($thumbWidth, $thumbHeight);
imagecopyresampled(
$thumbnail,
$this->image,
0, 0, 0, 0,
$thumbWidth,
$thumbHeight,
$this->getWidth(),
$this->getHeight());
$this->image = $thumbnail;
}
/**
* {@inheritDoc}
* @see \LAM\ImageUtils\ImageManipulator::getImageData()
*/
public function getImageData() {
ob_start();
if ($this->type == IMAGETYPE_JPEG) {
imagejpeg($this->image);
}
else if ($this->type == IMAGETYPE_PNG) {
imagepng($this->image);
}
$output = ob_get_contents();
ob_clean();
return $output;
}
}
?>

View File

@ -2,7 +2,7 @@
namespace LAM\PDF;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2017 Roland Gruber
Copyright (C) 2017 - 2018 Roland Gruber
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -69,7 +69,6 @@ class LAMTCPDF extends \TCPDF {
$logoFile = $this->structure->getLogo();
if (!empty($logoFile) && ($logoFile !== 'none')) {
$logo = dirname(__FILE__) . "/../config/pdf/" . $_SESSION['config']->getName() . "/logos/" . $logoFile;
$imgProperties = getimagesize($logo);
$this->Image($logo, 10, 10, '', 15, 'JPG', '', 'T', false, 300, '', false, false, 0, false, false, false);
}
else {

View File

@ -1,6 +1,7 @@
<?php
use \LAM\TYPES\TypeManager;
use LAM\ImageUtils\ImageManipulationFactory;
use LAM\PDF\PDFImage;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
@ -556,6 +557,9 @@ class inetOrgPerson extends baseModule implements passwordService {
if (!$this->isBooleanConfigOptionSet('inetOrgPerson_hideInitials')) {
$return['PDF_fields']['initials'] = _('Initials');
}
if (!$this->isBooleanConfigOptionSet('inetOrgPerson_hidejpegPhoto')) {
$return['PDF_fields']['jpegPhoto'] = _('Photo');
}
// help Entries
$return['help'] = array (
'description' => array (
@ -2014,6 +2018,7 @@ class inetOrgPerson extends baseModule implements passwordService {
else if (isset($this->attributes['INFO.userPasswordClearText'])) {
$this->addPDFKeyValue($return, 'userPassword', _('Password'), $this->attributes['INFO.userPasswordClearText']);
}
$this->addPDFImage($return, 'jpegPhoto');
return $return;
}

View File

@ -1,5 +1,7 @@
<?php
namespace LAM\PDF;
use LAM\ImageUtils\ImageManipulationFactory;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2004 Michael Duergner
@ -144,6 +146,9 @@ function createPdf($structure, $accounts, $pdfKeys, $account_type, $font, $retur
else if ($valueEntry instanceof PDFTable) {
printTable($pdf, $valueEntry, $font);
}
else if ($valueEntry instanceof PDFImage) {
printImage($pdf, $valueEntry, $font);
}
}
}
}
@ -231,6 +236,28 @@ function printTable(&$pdf, $table, $fontName) {
$pdf->Ln(LAMPDF_LINEHEIGHT);
}
/**
* Prints a PDFLabelValue entry.
*
* @param LAMTCPDF $pdf PDF
* @param PDFImage $imageEntry entry
* @param string $fontName font name
*/
function printImage(&$pdf, $imageEntry, $fontName) {
include_once dirname(__FILE__) . '/imageutils.inc';
$imageManipulator = ImageManipulationFactory::getImageManipulator($imageEntry->getImageData());
$height = $imageManipulator->getHeight() / 2.9;
if ($height > 40) {
$height = 40;
}
$pdf->Image('@' . $imageManipulator->getImageData(), null, null, null, $height,
'JPG', null, 'T', true, 300, 'R',
false, false, 0, false, false, false);
$pdf->Ln($height);
$pdf->Ln(LAMPDF_LINEHEIGHT);
$imageManipulator = null;
}
/**
* Common interface for all PDF entries.
*
@ -381,3 +408,41 @@ class PDFLabelValue implements PDFEntry {
}
}
/**
* Adds an image to the PDF file.
*
* @package PDF
* @author Roland Gruber
*/
class PDFImage implements PDFEntry {
private $binaryData;
/**
* Constructor
*
* @param string $binaryData image data
*/
public function __construct($binaryData) {
$this->binaryData = $binaryData;
}
/**
* {@inheritDoc}
* @see \LAM\PDF\PDFEntry::getHeadline()
*/
public function getHeadline() {
return '';
}
/**
* Returns the image data.
*
* @return string image data
*/
public function getImageData() {
return $this->binaryData;
}
}

View File

@ -3,6 +3,7 @@ namespace LAM\PDF;
use \htmlStatusMessage;
use \LAMException;
use \LAM\ImageUtils\ImageManipulationFactory;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
Copyright (C) 2003 - 2006 Michael Duergner
@ -102,12 +103,13 @@ function getAvailableLogos() {
$dirHandle = opendir($dirPath);
while($file = readdir($dirHandle)) {
if(!is_dir($file) && $file != '.' && $file != '..' && preg_match('/\\.(jpg|png)$/i',$file)) {
$infos = getimagesize($dirPath . $file);
if($infos[0] <= 2000 && $infos[1] <= 300) {
include_once dirname(__FILE__) . '/imageutils.inc';
$imageManipulator = ImageManipulationFactory::getImageManipulatorFromFile($dirPath . $file);
$infos = array($imageManipulator->getWidth(), $imageManipulator->getHeight());
$imageManipulator = null;
array_push($return, array('filename' => $file, 'infos' => $infos));
}
}
}
sort($return);
return $return;
}
@ -169,8 +171,6 @@ function uploadPDFLogo($file, $name) {
if (!preg_match('/[a-zA-Z0-9_-]+\\.(png)|(jpg)/i', $name)) {
return new htmlStatusMessage('ERROR', _('Unable to upload logo file.'), _('The file name must end with ".png" or ".jpg".'));
}
$infos = getimagesize($file);
if ($infos[0] <= 2000 && $infos[1] <= 300) {
$dirPath = dirname(__FILE__) . '/../config/pdf/' . $_SESSION['config']->getName() . '/logos/';
$success = copy($file, $dirPath . '/' . $name);
if ($success) {
@ -180,8 +180,6 @@ function uploadPDFLogo($file, $name) {
return new htmlStatusMessage('ERROR', _('Unable to upload logo file.'), $name);
}
}
return new htmlStatusMessage('ERROR', _('Unable to upload logo file.'), _('The file must not exeed 2000x300px.'));
}
/**
* Deletes a PDF logo file.

View File

@ -1,4 +1,6 @@
<?php
use LAM\ImageUtils\ImageManipulationFactory;
/*
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
@ -754,15 +756,18 @@ class lamUserList extends lamList {
fwrite($outjpeg, $entry[$attribute][0]);
fclose ($outjpeg);
$photoFile = '../../tmp/' . $jpeg_filename;
$imgSize = getimagesize($photoFile);
include_once dirname(__FILE__) . '/../imageutils.inc';
$imageManipulator = ImageManipulationFactory::getImageManipulator($entry[$attribute][0]);
$imgHeight = $imageManipulator->getHeight();
$imageManipulator = null;
$minSize = 64;
if ($imgSize[0] < 64) {
$minSize = $imgSize[0];
if ($imgHeight < 64) {
$minSize = $imgHeight;
}
$imgTitle = _('Click to switch between thumbnail and original size.');
echo "<img id=\"img$imgNumber\" title=\"$imgTitle\" height=$minSize src=\"" . $photoFile . "\" alt=\"" . _('Photo') . "\">";
echo '<script type="text/javascript">';
echo "addResizeHandler(document.getElementById(\"img$imgNumber\"), $minSize, " . $imgSize[1] . ")";
echo "addResizeHandler(document.getElementById(\"img$imgNumber\"), $minSize, " . $imgHeight . ")";
echo '</script>';
}
elseif (($attribute == 'mail') || ($attribute == 'rfc822Mailbox')) {