Issue #1142620: imagefield_widget_validate does not exist.
[project/imagefield.git] / imagefield.module
1 <?php
2
3 /**
4 * @file
5 * ImageField core hooks and menu callbacks.
6 */
7
8 require_once dirname(__FILE__) . '/imagefield_file.inc';
9 require_once dirname(__FILE__) . '/imagefield_widget.inc';
10
11 /**
12 * Implementation of hook_theme().
13 */
14 function imagefield_theme() {
15 return array(
16 // Theme an image uploaded to ImageField with alt and title.
17 // TODO: Switch to core theme image if possible.
18 'imagefield_image' => array(
19 'arguments' => array('file' => NULL, 'alt' => '', 'title' => '', 'attributes' => NULL, 'getsize' => TRUE),
20 ),
21 // Theme an ImageField field item. It calls imagefied_image with the proper
22 // item properties as arguments.
23 'imagefield_item' => array(
24 'arguments' => array('item' => NULL),
25 ),
26 // imagefield_widget form element type theme function.
27 'imagefield_widget' => array(
28 'arguments' => array('element' => NULL),
29 'file' => 'imagefield_widget.inc',
30 ),
31 // Use to generate a preview (admin view) of an imagefield item for use in
32 // field item forms and filefield widgets. Invoked by filefield_widget_process.
33 'imagefield_widget_preview' => array(
34 'arguments' => array('item' => NULL),
35 ),
36 // Theme function for the field item elements. allows you to place children
37 // within the context of the parent.
38 'imagefield_widget_item' => array(
39 'arguments' => array('element' => NULL),
40 ),
41 // Generates and img tag to the admin thumbnail of an ImageField upload.
42 'imagefield_admin_thumbnail' => array(
43 'arguments' => array('item' => NULL),
44 ),
45 // ImageField formatter theme functions.
46 'imagefield_formatter_image_plain' => array(
47 'arguments' => array('element' => NULL),
48 'file' => 'imagefield_formatter.inc',
49 ),
50 'imagefield_formatter_image_nodelink' => array(
51 'arguments' => array('element' => NULL),
52 'file' => 'imagefield_formatter.inc',
53 ),
54 'imagefield_formatter_image_imagelink' => array(
55 'arguments' => array('element' => NULL),
56 'file' => 'imagefield_formatter.inc',
57 ),
58 );
59 }
60
61 /**
62 * Implementation of hook_elements().
63 */
64 function imagefield_elements() {
65 $elements = array();
66
67 // Catch problems when this is called too early during installation or update.
68 if (!module_exists('filefield')) {
69 return $elements;
70 }
71
72 // An ImageField is really just a FileField with extra processing.
73 $filefield_elements = module_invoke('filefield', 'elements');
74 $elements['imagefield_widget'] = $filefield_elements['filefield_widget'];
75 $elements['imagefield_widget']['#process'][] = 'imagefield_widget_process';
76
77 // ImageField needs a separate value callback to save its alt and title texts.
78 $elements['imagefield_widget']['#value_callback'] = 'imagefield_widget_value';
79
80 return $elements;
81 }
82
83 /**
84 * Implementation of hook_file_download.
85 */
86 function imagefield_file_download($filepath) {
87 // Return headers for admin thumbnails if private files are enabled.
88 if (strpos($filepath, 'imagefield_thumbs') !== FALSE) {
89 $original_path = str_replace('imagefield_thumbs/', '', $filepath);
90 $original_full_path = file_create_path($original_path);
91 $thumb_full_path = file_create_path($filepath);
92
93 // Allow access to temporary thumbnails, since they're not yet associated
94 // with a node. If not temporary, check access on the original file.
95 $status = db_result(db_query("SELECT status FROM {files} WHERE filepath = '%s'", $original_full_path));
96 $access = ($status == 0 || !in_array(-1, module_invoke_all('file_download', $original_path)));
97 if ($access && $info = getimagesize($thumb_full_path)) {
98 return array(
99 'Content-Type: ' . $info['mime'],
100 'Content-Length: ' . filesize($thumb_full_path)
101 );
102 }
103 }
104
105 // Return headers for default images.
106 if (strpos($filepath, 'imagefield_default_images') !== FALSE) {
107 $full_path = file_create_path($filepath);
108 if ($info = getimagesize($full_path)) {
109 return array(
110 'Content-Type: ' . $info['mime'],
111 'Content-Length: ' . filesize($full_path)
112 );
113 }
114 }
115 }
116
117 /**
118 * Implementation of hook_nodeapi().
119 *
120 * Add ALT and title texts to the search index.
121 */
122 function imagefield_nodeapi($node, $op) {
123 if ($op == 'update index') {
124 static $fields;
125 if (!isset($fields)) {
126 $fields = filefield_get_field_list();
127 }
128
129 $texts = array();
130 foreach ($fields as $field) {
131 $name = $field['field_name'];
132 // Check this node for ImageField alt and title data.
133 if (isset($node->$name) && is_array($node->$name)) {
134 foreach ($node->$name as $item) {
135 $texts[] = isset($item['data']['alt']) ? $item['data']['alt'] : '';
136 $texts[] = isset($item['data']['title']) ? $item['data']['title'] : '';
137 }
138 }
139 }
140 return implode(' ', $texts);
141 }
142 }
143
144 /**
145 * Implementation of CCK's hook_widget_info().
146 */
147 function imagefield_widget_info() {
148 $module_path = drupal_get_path('module', 'imagefield');
149 return array(
150 'imagefield_widget' => array(
151 'label' => t('Image'),
152 'field types' => array('filefield'),
153 'multiple values' => CONTENT_HANDLE_CORE,
154 'callbacks' => array('default value' => CONTENT_CALLBACK_CUSTOM),
155 'description' => t('An edit widget for image files, including a preview of the image.'),
156 ),
157 );
158 }
159
160 /**
161 * Implementation of CCK's hook_widget_settings().
162 */
163 function imagefield_widget_settings($op, $widget) {
164 switch ($op) {
165 case 'form':
166 return imagefield_widget_settings_form($widget);
167 case 'validate':
168 return imagefield_widget_settings_validate($widget);
169 case 'save':
170 return imagefield_widget_settings_save($widget);
171 }
172 }
173
174 /**
175 * Implementation of CCK's hook_widget().
176 *
177 * Assign default properties to item and delegate to FileField.
178 */
179 function imagefield_widget(&$form, &$form_state, $field, $items, $delta = 0) {
180 // Add default values to items.
181 // TODO: use CCK's default value callback.
182 if (empty($items[$delta])) {
183 $items[$delta] = array('alt' => '', 'title' => '');
184 }
185
186 // Start with the FileField widget as a basic start.
187 // Note that FileField needs to modify $form by reference.
188 $element = filefield_widget($form, $form_state, $field, $items, $delta);
189
190 // Add ImageField specific validators.
191 $element['#upload_validators'] = array_merge($element['#upload_validators'], imagefield_widget_upload_validators($field));
192
193 return $element;
194 }
195
196 /**
197 * Get the additional upload validators for an image field.
198 *
199 * @param $field
200 * The CCK field array.
201 * @return
202 * An array suitable for passing to file_save_upload() or the file field
203 * element's '#upload_validators' property.
204 */
205 function imagefield_widget_upload_validators($field) {
206 $validators = array();
207
208 // Match the default value if no file extensions have been saved at all.
209 if (!isset($field['widget']['file_extensions'])) {
210 $field['widget']['file_extensions'] = 'png gif jpg jpeg';
211 }
212
213 // Ensure that only web images are supported.
214 $web_extensions = array('png', 'gif', 'jpg', 'jpeg');
215 $extensions = array_filter(explode(' ', $field['widget']['file_extensions']));
216 if (empty($extensions)) {
217 $extensions = $web_extensions;
218 }
219 $validators['filefield_validate_extensions'][0] = implode(' ', array_intersect($extensions, $web_extensions));
220
221 // Add the image validator as a basic safety check.
222 $validators['filefield_validate_is_image'] = array();
223
224 // Add validators for resolutions.
225 if (!empty($field['widget']['max_resolution']) || !empty($field['widget']['min_resolution'])) {
226 $validators['filefield_validate_image_resolution'] = array(
227 $field['widget']['max_resolution'],
228 $field['widget']['min_resolution'],
229 );
230 }
231
232 return $validators;
233 }
234
235 /**
236 * Implementation of CCK's hook_field_formatter_info().
237 */
238 function imagefield_field_formatter_info() {
239 $module_path = drupal_get_path('module', 'imagefield');
240 $formatters = array(
241 'image_plain' => array(
242 'label' => t('Image'),
243 'field types' => array('filefield'),
244 'description' => t('Displays image files in their original size.'),
245 ),
246 'image_nodelink' => array(
247 'label' => t('Image linked to node'),
248 'field types' => array('filefield'),
249 'description' => t('Displays image files in their original size.'),
250 ),
251 'image_imagelink' => array(
252 'label' => t('Image linked to file'),
253 'field types' => array('filefield'),
254 'description' => t('Displays image files in their original size.'),
255 ),
256 );
257 return $formatters;
258 }
259
260 /**
261 * Implementation of CCK's hook_default_value().
262 */
263 function imagefield_default_value(&$form, &$form_state, $field, $delta) {
264 return filefield_default_value($form, $form_state, $field, $delta);
265 }
266
267 /**
268 * Implementation of hook_form_[form_id]_alter().
269 *
270 * Modify the add new field form to make "Image" the default formatter.
271 */
272 function imagefield_form_content_field_overview_form_alter(&$form, &$form_state) {
273 $form['#submit'][] = 'imagefield_form_content_field_overview_submit';
274 }
275
276 /**
277 * Submit handler to set a new field's formatter to "image_plain".
278 */
279 function imagefield_form_content_field_overview_submit(&$form, &$form_state) {
280 if (isset($form_state['fields_added']['_add_new_field']) && isset($form['#type_name'])) {
281 $new_field = $form_state['fields_added']['_add_new_field'];
282 $node_type = $form['#type_name'];
283 $field = content_fields($new_field, $node_type);
284 if ($field['widget']['module'] == 'imagefield') {
285 foreach ($field['display_settings'] as $display_type => $display_settings) {
286 if ($field['display_settings'][$display_type]['format'] == 'default') {
287 $field['display_settings'][$display_type]['format'] = 'image_plain';
288 }
289 }
290 content_field_instance_update($field);
291 }
292 }
293 }
294
295 /**
296 * Implementation of hook_filefield_data_info().
297 */
298 function imagefield_filefield_data_info() {
299 return array(
300 'alt' => array(
301 'title' => t('Alt text'),
302 'callback' => 'check_plain',
303 ),
304 'title' => array(
305 'title' => t('Title'),
306 'callback' => 'check_plain',
307 ),
308 );
309 }
310
311 /**
312 * @defgroup "Theme Callbacks"
313 * @{
314 * @see imagefield_theme().
315 */
316 function theme_imagefield_image($file, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
317 $file = (array) $file;
318
319 if ($getsize) {
320 // Use cached width and height if available.
321 if (!empty($file['data']['width']) && !empty($file['data']['height'])) {
322 $attributes['width'] = $file['data']['width'];
323 $attributes['height'] = $file['data']['height'];
324 }
325 // Otherwise pull the width and height from the file.
326 elseif (list($width, $height, $type, $image_attributes) = @getimagesize($file['filepath'])) {
327 $attributes['width'] = $width;
328 $attributes['height'] = $height;
329 }
330 }
331
332 if (!empty($title)) {
333 $attributes['title'] = $title;
334 }
335
336 // Alt text should be added even if it is an empty string.
337 $attributes['alt'] = $alt;
338
339 // Add a timestamp to the URL to ensure it is immediately updated after editing.
340 $query_string = '';
341 if (isset($file['timestamp'])) {
342 $query_character = (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE && variable_get('clean_url', '0') == '0') ? '&' : '?';
343 $query_string = $query_character . $file['timestamp'];
344 }
345
346 // Encode the path so that unusual characters are printed correctly.
347 $path = field_file_urlencode_path($file['filepath']);
348
349 // Construct the URL.
350 $url = file_create_url($path) . $query_string;
351 $attributes['src'] = $url;
352 $attributes = drupal_attributes($attributes);
353 return '<img '. $attributes .' />';
354 }
355
356 function theme_imagefield_item($item) {
357 return theme('imagefield_image', $item, $item['alt'], $item['title']);
358 }
359
360 function theme_imagefield_widget_preview($item = NULL) {
361 return '<div class="imagefield-preview">' . theme('imagefield_admin_thumbnail', $item) . '</div>';
362 }
363
364 function theme_imagefield_widget_item($element) {
365 return theme('filefield_widget_item', $element);
366 }
367
368 function theme_imagefield_admin_thumbnail($item = NULL) {
369 if (is_null($item) || empty($item['filepath'])) {
370 return '<!-- link to default admin thumb -->';
371 }
372 $thumb_path = imagefield_file_admin_thumb_path($item);
373
374 // Encode the path so that unusual characters are printed correctly.
375 $thumb_path = field_file_urlencode_path($thumb_path);
376
377 // Add a timestamp to the URL to ensure it is immediately updated after editing.
378 $query_string = '';
379 if (isset($item['timestamp'])) {
380 $query_character = (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE && variable_get('clean_url', '0') == '0') ? '&' : '?';
381 $query_string = $query_character . $item['timestamp'];
382 }
383
384 return '<img src="'. file_create_url($thumb_path) . $query_string . '" title="' . check_plain($item['filename']) . '" alt="' . t('Image preview') . '" />';
385 }
386 /**
387 * @} End defgroup "Theme Callbacks".
388 */