/[drupal]/contributions/modules/image_import/image_import.module
ViewVC logotype

Contents of /contributions/modules/image_import/image_import.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.18 - (show annotations) (download) (as text)
Tue Jun 6 03:15:29 2006 UTC (3 years, 5 months ago) by syscrusher
Branch: MAIN
CVS Tags: HEAD
Changes since 1.17: +22 -10 lines
File MIME type: text/x-php
Committed patch from jsloan to make module functional with Drupal 4.7.
Some work is still needed, and there is a conflict with at least one other
module (image_exact). This is definitely a "beta", but it does work.
More details on the image_exact conflict at issue #63252.
1 <?php
2 // $Id: image_import.module,v 1.17 2005/11/20 03:31:19 syscrusher Exp $
3
4 /**
5 * image_import.module BETA release -- not for production use
6 * Maintainer: Scott Courtney (scott@4th.com) "syscrusher" on drupal.org
7 *
8 * This module is an adjunct to image.module by James Walker ("walkah").
9 *
10 * WARNING: This module calls one private function, _image_build_derivatives(),
11 * from image.module. If that is deprecated or changed, this module will break.
12 */
13
14 // Constants to decouple this module from changes to others
15 define('IMAGE_IMPORT_IMAGE_NODETYPE','image');
16 define('IMAGE_IMPORT_STATUS_ABORT',-1);
17 define('IMAGE_IMPORT_STATUS_ERROR',-2);
18 define('IMAGE_IMPORT_STATUS_OK',1);
19 define('IMAGE_IMPORT_STATUS_OK_CAPTION',2);
20 define('IMAGE_IMPORT_STATUS_OK_TIMEOUT',3);
21
22 /**
23 * Implementation of hook_help
24 */
25 function image_import_help($section) {
26 global $user;
27 global $base_url;
28 switch ($section) {
29 case 'admin/modules#description':
30 return t('Allows mass import of images from a directory on the server.');
31 case 'node/add#image_import':
32 return t('Import multiple images and (optionally) captions from a directory on the web server into %site. This feature allows you to upload an entire gallery of images using FTP, SSH, or other tools, then import all of them in one step. If you are new to this feature, you may find the %helplink useful.',array('%site'=>variable_get('site_name',t('this web site')), '%helplink'=>l(t('detailed instructions'),'node/add/image_import/help')));
33 case 'admin/image_import':
34 return t('Image import allows users who have uploaded image (and optionally, caption) files to a directory on the server to mass-import these as image nodes.');
35 case 'node/add/image_import/help':
36 $path = image_import_get_import_path(TRUE);
37 $upload_path = image_import_get_upload_path();
38 $upload_host = preg_replace('#^\w+://(.*)/?.*$#','\1',$base_url);
39 if (empty($upload_host)) {
40 $upload_host = 'www.example.com';
41 }
42 $can_override = user_access('override image import path');
43 $max_res = variable_get('upload_max_resolution','');
44 $max_size = _image_import_max_upload_size();
45 $res = explode('x',$max_res);
46 $h_res = empty($res[0]) ? t('(unlimited)') : $res[0];
47 $v_res = empty($res[1]) ? t('(unlimited)') : $res[1];
48 $sizetext = $max_size ? t('may be up to %size megabytes in size', array('%size'=>$max_size)) : t('are limited in size only by the web server configuration');
49 $html = '<p>' . t('This feature is designed to allow you to import many images into a gallery in one step, rather than creating them one-at-a-time. This only makes sense if the images share common attributes, such as relating to the same set of topics and being part of the same gallery or album. To create multiple galleries, you will need to repeat all the steps in these instructions for each gallery.');
50 $html .= '<p>' . t('The %formlink provides specific instructions for filling in the form to do an import. This additional help explains the overall process in more detail than will fit on the import page itself.', array('%formlink'=>l(t('image import form'), 'node/add/image_import', array('title'=>t('Begin importing your images')))));
51 $html .= '<p>' . t('Here are the steps for importing all the images for one gallery. This may seem complicated the first time you do it, but remember that you can import a large number of images at once, so in the long run this feature will save you a lot of time. Also, the steps become easier once you have done this a couple of times.') . '<ol>';
52 $html .= '<li><p>' . t('If it does not already exist, make sure you or the site administrator create the directory &quot;%dir&quot; underneath the top-level directory for this site. If you are not the site administrator, that person will need to tell you how to access that directory for uploading your files.', array('%dir'=>$path));
53 if ($can_override) {
54 $html .= '<p>' . t(' (Your user privileges allow you to override the import directory path, but whatever directory you choose must be located underneath the top-level directory for the web site and must be readable and writeable by the web server.)');
55 }
56 $html .= '<li><p>' . t('Prepare your images on your personal computer or workstation, cropping, scaling, and editing them as you prefer using your choice of image editing software. (You may wish to make a copy of your originals and then edit the copy for importing to the web galleries.) The version of each image that you import should be the <em>largest size</em> that you wish to make available on the web. Do not worry about creating thumbnails or other scaled-down versions, because the import process will do that automatically for you.');
57 $html .= '<p>' . t('Images on this site are limited to a maximum resolution of %h x %v (<i>horizontal</i> x <i>vertical</i>) pixels and %sizetext.', array('%h'=>$h_res, '%v'=>$v_res, '%sizetext'=>$sizetext));
58 $html .= t(' The allowed file extensions for images are: %ext.', array('%ext'=>implode(', ',_image_import_img_extensions())));
59 $html .= '<li><p>' . t('Optionally, add captions for some or all of your images by creating a file in the same directory with the same name as its corresponding image, but with one of the following extensions: %ext. For example, the caption for &quot;photo-0132.jpg&quot; might be &quot;photo-0132.txt&quot;.', array('%ext'=>implode(', ',_image_import_cap_extensions())));
60 $html .= '<p>' . t('For each image with a caption file, the import will read the contents of that file and add it as a description for the corresponding image in the gallery. Images without captions will still be imported, but their description will be blank.');
61 $html .= '<li><p>' . t('Upload all of your images and caption files to the import directory. From an FTP prompt, for example, you might need to &quot;<code>cd %updir</code>&quot; before doing &quot;<code>mput *</code>&quot; to send the files. If you use &quot;scp&quot; (part of OpenSSH) to upload, you might execute a command like this from your workstation, from the directory containing your files:<br><code>scp&nbsp;*&nbsp;%uname@%host:%updir</code>', array('%updir'=>$upload_path, '%uname'=>($user->name), '%host'=>$upload_host));
62 $html .= '<li><p>' . t('Browse to the %formlink, and follow the instructions to import your images and captions.', array('%formlink'=>l(t('image import form'), 'node/add/image_import', array('title'=>t('Begin importing your images')))));
63 $html .= '</ol>';
64 $html .= '<p>' . t('If you are importing many images at once, or your image files are really large, you may find that the import process (the last step in the list above) causes a timeout error from the web server. If that happens, either split your gallery into smaller groups of files and import each of these groups with the same category and gallery selections, or ask your site administrator to check the PHP settings for script execution time limits.');
65 // Need a trailing paragraph break in case there is site-specific guide
66 // text from the main node configuration page. Drupal core will
67 // automatically append that text.
68 $html .= '<p>';
69 return $html;
70 }
71 }
72
73 /**
74 * Implementation of hook_perm
75 */
76 function image_import_perm() {
77 return array('import images', 'import system images', 'override image import path');
78 }
79
80 /**
81 * Implementation of hook_access
82 */
83 function image_import_access($op, $node) {
84 switch ($op) {
85 case 'create': return (user_access('import images') || user_access('import system images'));
86 default:
87 return NULL;
88 }
89 }
90
91 /**
92 * Dummy implementation of hook_nodeapi
93 */
94 //function image_import_nodeapi(&$node, $op, $arg1, $arg2) {
95 // return;
96 //}
97
98 /**
99 * implentation of hook_node_info
100 */
101 function image_import_node_info() {
102 return array('image_import' => array('name' => 'image_import', 'base' => 'image_import'));
103 }
104
105 /**
106 * Implementation of hook_settings
107 */
108 function image_import_settings() {
109 // First, check the status of the directory path and report any problems
110 _image_import_check_settings(FALSE, 'image_import_default_import_path');
111
112 $form = array();
113 $form['file_path'] = array(
114 '#type' => 'fieldset',
115 '#title' => t('File path settings'),
116 '#description' => t('These settings control the path on the server from which image and caption files will be imported. You can put the string <code>%u</code> or <code>%U</code> in the path fields, and these strings will be replaced by the username of the person doing the import. %U and %u are the same except that %U automatically removes space, %, &lt;, &gt;, *, /, \, and ? from the username.'),
117 '#tree' => FALSE,
118 '#collapsible' => TRUE,
119 '#collapsed' => FALSE,
120 );
121 $form['file_path']['image_import_default_import_path'] = array(
122 '#type' => 'textfield',
123 '#title' => t('Default import path'),
124 '#description' => t('<p>Subdirectory beneath the Drupal directory from which images may be imported in bulk. Drupal must have read/write access to this directory and subdirectories beneath it. This path must be <strong>different</strong> from the %imgsettings, which on this site is currently set to &quot;%imgpath&quot;</strong>.<p>Users with the &quot;override image import path&quot; permission can manually override this setting.', array('%imgsettings'=>l(t('image default path'), 'admin/settings/image'), '%imgpath'=>_image_import_get_image_path())),
125 '#default_value' => variable_get('image_import_default_import_path','image_import/%U'),
126 '#maxlength' => 50,
127 '#size' => 30,
128 '#required' => TRUE,
129 );
130 $form['file_path']['image_import_default_upload_path'] = array(
131 '#type' => 'textfield',
132 '#title' => t('Default import path'),
133 '#description' => t('<p>This should be set to the default import path (see above), but as viewed by a typical user during an FTP or similar upload. This may be either an absolute path or a path relative to the user\'s system home directory.<p>If you specify this setting, the interactive help for this module will use it in the text explaining how to upload files, customizing the help text as needed for each individual. The setting is not used for anything except generating help text.'),
134 '#default_value' => variable_get('image_import_default_upload_path',''),
135 '#maxlength' => 50,
136 '#size' => 30,
137 );
138 $form['file_path']['image_import_system_nou'] = array(
139 '#type' => 'checkbox',
140 '#title' => t('Ignore %u and %U for system user'),
141 '#description' => t('<p>If checked, the special administrative user (the first account created when the site was set up) will not be affected by <code>%u</code> or <code>%U</code> in the import path setting. Note that the system user can <i>always</i> override the path if they wish, because that special account has all available privileges.'),
142 '#return_value' => '1',
143 '#default_value' => variable_get('image_import_system_nou', '0'),
144 );
145 $form['image_import_force_delete'] = array(
146 '#type' => 'checkbox',
147 '#title' => t('Force deletion of upload files'),
148 '#description' => t('<p>If checked, this option forces image_import to delete any files from the upload directory that have been successfully imported. This is strongly recommended, because if they are not deleted, the next import from the same server directory may re-import these files and create duplicate image nodes. If this option is disabled, users will be offered a choice of whether to keep or delete the files.'),
149 '#return_value' => '1',
150 '#default_value' => variable_get('image_import_force_delete', '1'),
151 );
152 $form['advanced'] = array(
153 '#type' => 'fieldset',
154 '#title' => t('Advanced settings'),
155 '#description' => t('These settings can normally be left at the default values'),
156 '#tree' => FALSE,
157 '#collapsible' => TRUE,
158 '#collapsed' => TRUE,
159 );
160 $form['advanced']['image_import_debug'] = array(
161 '#type' => 'checkbox',
162 '#title' => t('Debug Mode'),
163 '#description' => t('<p>Turn this option on to enable verbose error checking and detailed messages. May be confusing to novice users, but extremely useful if you are having problems with importing images.'),
164 '#return_value' => '1',
165 '#default_value' => variable_get('image_import_debug', '0'),
166 );
167 $form['advanced']['image_import_adjust_order'] = array(
168 '#type' => 'checkbox',
169 '#title' => t('Adjust timestamp order of images'),
170 '#description' => t('By default, imported images are marked as created at the actual time they are imported. Since images are imported in filename order, this may cause some albums or galleries to appear in reverse order. Checking this option causes the import to apply a reversed-order timestamp to correct this, though that means the displayed creation timewill differ by a few seconds from the actual creation time. This setting establishes a default for this option, but users can override that default for each import, if they prefer.'),
171 '#return_value' => '1',
172 '#default_value' => variable_get('image_import_adjust_order', '0'),
173 );
174 $form['advanced']['image_import_timeout'] = array(
175 '#type' => 'select',
176 '#title' => t('Time limit per run (seconds)'),
177 '#description' => t('If you are having problems with the import script timing out on large directories, and you are not able to modify the web server or PHP parameters to fix the problem, you can enable this option to set a limit on how long image import will process images during a single run. If the time limit is reached, the user can simply re-run the same job again to do the next group of images, repeating until the entire directory has been imported. <strong>This option is ignored unless you and/or the user enable deletion of images after import, because otherwise you would import the same files over and over.</strong> It is recommended to set this option to slightly <em>less than</em> the PHP <code>max_execution_time</code> limit. On this site, <code>max_execution_time</code>=%met seconds currently. The PHP <code>max_execution_time</code> setting is usually changed in php.ini or httpd.conf (server-wide) or (if allowed) the .htaccess file for this site.', array('%met'=>ini_get('max_execution_time'))),
178 '#options' => drupal_map_assoc(array(15, 20, 30, 45, 60, 90, 120, 180, 240, 300)),
179 '#default_value' => variable_get('image_import_timeout', '0'),
180 );
181 $form['text1'] = array(
182 '#type' => 'fieldset',
183 '#title' => t('Security settings'),
184 '#description' => t('<p>You will need to define appropriate %roles that have %permission to upload files, then establish an appropriate list of permitted %extensions for each of these roles.', array('%roles'=>l(t('security roles'),'admin/access/roles'), '%permission'=>l(t('permission'),'admin/access'), '%extensions'=>l(t('file extensions'),'admin/settings/upload'))),
185 '#collapsible' => TRUE,
186 '#collapsed' => FALSE,
187 );
188
189 return $form;
190 }
191
192 /**
193 * Checks the path settings and reports any problems detected.
194 * $with_post (default TRUE) is passed to image_import_get_import_path()
195 * (q.v.). $path_fieldname indicates the fieldname with which any path
196 * error should be associated (the outer subscript 'edit' is implied and
197 * should not be included in the fieldname). The fieldname defaults to none.
198 */
199 function _image_import_check_settings($with_post=TRUE, $path_fieldname='') {
200 $filepath = variable_get('file_directory_path','');
201 if (empty($filepath)) {
202 drupal_set_message(t('Upload module has not yet been configured.'),'error');
203 return FALSE;
204 }
205 $pathsetting = variable_get('image_import_default_import_path','');
206 if (empty($pathsetting)) {
207 drupal_set_message(t('Image import module has not yet been configured.'),'error');
208 return FALSE;
209 }
210 $image_path = _image_import_get_image_path();
211 if ($pathsetting == $image_path) {
212 drupal_set_message(t('Image import path may not be the same as the image path.'),'error');
213 return FALSE;
214 }
215 if (! user_access('upload files')) {
216 drupal_set_message(t('You do not have permission to upload files, which is a prerequisite for image import.'),'error');
217 return FALSE;
218 }
219 $extensions = _image_import_img_extensions();
220 if (count($extensions) < 1) {
221 drupal_set_message(t('Your user account has not been configured to allow any upload extensions. The site administrator will need to fix this for you in the settings for the "upload" module.'),'error');
222 return FALSE;
223 }
224 $path = image_import_get_import_path($with_post);
225 if (! module_exist('image')) {
226 drupal_set_message(t('Image module, a prerequisite for image import, is not installed or is disabled.'),'error');
227 return FALSE;
228 }
229 if (! is_writable($path)) {
230 drupal_set_message(t('The image import directory, %p, is missing or is not writable by the web server.', array('%p'=>$path)),'error');
231 if (user_access('override image import path')) {
232 drupal_set_message(t('Directory must be changed before import will work.'),'warning');
233 } else {
234 return FALSE;
235 }
236 }
237 $ok = file_check_directory($path, FALSE, $path_fieldname);
238 return $ok;
239 }
240
241 /**
242 * Scans a multi-level array with term ID numbers buried inside arrays
243 * inside arrays inside arrays...etc., and returns a simple array with just
244 * the tid numbers linearized plus, optionally, any free-tags entered by the
245 * user if a free-tag string is present in the input array.
246 *
247 * WARNING: This function uses recursion; modify with care or an infinite
248 * recursion loop is possible.
249 *
250 * For the outermost (non-recursed) invocation only, the parameter $keep_tags
251 * (default TRUE) tells the function to retain, unmodified, any element of
252 * the $tid_array whose subscript is 'tags'. This causes the subarray placed
253 * by the free-tag patch to taxonomy.module to work correctly across previews.
254 * Note that $tid_array['tags'] is a sub-array containing one or more string
255 * element(s), indexed by vocabulary ID (vid), with the free-form tags.
256 *
257 * If your installation does not have the free-tag support, the $keep_tags
258 * parameter has no effect since $tid_array['tags'] will not be present.
259 * It is necessary to disable $keep_tags only if you want to ensure that the
260 * returned array from this function has only existing tids in it.
261 */
262 function _image_import_extract_tids($tid_array, $keep_tags=TRUE) {
263 $tids = array();
264 if (is_array($tid_array)) {
265 foreach ($tid_array as $key=>$element) {
266 if (is_int($key)) {
267 if (is_array($element)) {
268 // $keep_tags is NEVER useful when recursing
269 $tids = array_merge($tids, _image_import_extract_tids($element, FALSE));
270 } else {
271 // Toss out zero values
272 if (intval($element)) {
273 $tids[] = $element;
274 }
275 }
276 }
277 }
278 }
279 if ($keep_tags && isset($tid_array['tags'])) {
280 $tids['tags'] = $tid_array['tags'];
281 }
282 return $tids;
283 }
284
285 /**
286 * This internal function looks up a variable containing a default path for
287 * something (optionally defaulting to the value in the second parameter)
288 * and then applies this module's path mangling rules for replaceable
289 * parameters and other features, plus some syntax cleanup.
290 */
291 function _image_import_get_path($variable_name, $variable_default_value='') {
292 global $user;
293 $uname = $user->name;
294 // If this is the system user, and settings instruct ignoring %u and %U for that
295 // user, then force $uname to be empty.
296 if ($user->uid == 1 && variable_get('image_import_system_nou',FALSE)) {
297 $uname = '';
298 }
299 // Remove space, %, <, >, *, /, \, and ? from the username for %U
300 $uname_mod = preg_replace('#[%<>*\\\\ \/?]#','',$uname,-1);
301 $pathsetting = variable_get($variable_name, $variable_default_value);
302 $path = trim(preg_replace('/%u/',$uname,$pathsetting,-1));
303 $path = trim(preg_replace('/%U/',$uname_mod,$pathsetting,-1));
304 if (! preg_match('#/$#', $path)) {
305 $path .= '/';
306 }
307 // Remove any double-slashes
308 $path = preg_replace('#//#','/',$path,-1);
309 return $path;
310 }
311
312 /**
313 * Returns the current import path for this user (may be the same as the system
314 * default, if "%u"/"%U" wasn't used in the system path setting). The returned path
315 * will always have a trailing slash.
316 *
317 * If $with_post is TRUE (default FALSE), then the value of $_POST['edit']['import_path']
318 * will override the other sources of settings *if* the user has the correct permissions.
319 */
320 function image_import_get_import_path($with_post = FALSE) {
321 if (user_access('override image import path') && ! empty($_POST['edit']['import_path'])) {
322 $path = trim($_POST['edit']['import_path']);
323 if (! preg_match('#/$#', $path)) {
324 $path .= '/';
325 }
326 // Remove any double-slashes
327 $path = preg_replace('#//#','/',$path,-1);
328 return $path;
329 }
330 return _image_import_get_path('image_import_default_import_path','image_import/%u');
331 }
332
333 /**
334 * For the purpose of user-specific help text, this function returns this user's
335 * version of the default upload path. The default, if the config variable is
336 * missing or empty, is to return image_import_get_import_path().
337 *
338 * WARNING: Callers should NOT rely on this being any particular format, because
339 * it is strictly intended to create strings for help messages. The site admin
340 * is allowed to munge the syntax of the setting any way they wish to make it
341 * clear to their user community.
342 */
343 function image_import_get_upload_path() {
344 return _image_import_get_path('image_import_default_upload_path',
345 image_import_get_import_path());
346 }
347
348 /**
349 * Implementation of hook_menu
350 */
351 function image_import_menu($may_cache) {
352 global $user;
353 $items = array();
354 if ($may_cache) {
355 $items[] = array('path' => 'node/add/image_import', 'title' => t('import images'),
356 'callback' => '_image_import_add',
357 'access' => user_access('import images'));
358 // $items[] = array('path' => 'admin/settings/image_import/legacy', 'title' => t('import from legacy image.module'),
359 // 'callback' => 'image_import_legacy',
360 // 'access' => ($user->uid == 1));
361 }
362 return $items;
363 }
364
365 /**
366 * Menu handler for the import functions
367 */
368 function _image_import_add() {
369 $html = '';
370 // Don't disable the processing of the form if we've already been there,
371 // because any "settings failure" will be due to settings in the form
372 // itself and not at the system level.
373 if (arg(3) == 'help') {
374 // Leave HTML alone and let image_import_help() handle it
375 } else if (_image_import_check_settings(TRUE, 'import_path') || is_array($_POST['edit'])) {
376 $html .= _image_import_form();
377 } else {
378 $html .= '<p>' . t('Image import is disabled until the %config problem is corrected.', array('%config'=>l('configuration', 'admin/settings/image_import')));
379 }
380 print(theme('page',"<p>".$html));
381 }
382
383 /**
384 * Helper function returns a form for initiating the upload
385 */
386 function _image_import_form() {
387 global $user;
388
389 if (is_array($_POST['edit'])) {
390 // Assume precheck if the button to actually do it was *not* pressed
391 $precheck = $_POST['op']!='Import images'?1:0;
392 $status = image_import_import($precheck);
393 // Some options set from POST or from system-wide defaults
394 $delete_after = $_POST['edit']['delete_after'];
395 $adjust_order = $_POST['edit']['adjust_order'];
396 } else {
397 // Some options set from POST or from system-wide defaults
398 $delete_after = variable_get('image_import_force_delete',FALSE);
399 $adjust_order = variable_get('image_import_adjust_order', 0);
400 }
401 $dummy_node =& new stdClass();
402 $dummy_node->type = IMAGE_IMPORT_IMAGE_NODETYPE;
403 // $dummy_node->taxonomy =& _image_import_extract_tids($_POST['edit']['taxonomy']);
404 $dummy_node->taxonomy = $_POST['edit']['taxonomy'];
405
406 $html = '<p>' . t('An explanation of how to import images is available on the %helplink page.',array('%helplink'=>l(t('detailed instructions'),'node/add/image_import/help')));
407
408 $form = array();
409 /*if ($taxonomy_form = taxonomy_node_form($dummy_node)) {
410 $form += $taxonomy_form;
411 }*/
412
413 $form['advanced'] = array(
414 '#type' => 'fieldset',
415 '#title' => t('Advanced settings'),
416 '#description' => t('These settings can normally be left at the default values'),
417 '#tree' => FALSE,
418 '#collapsible' => TRUE,
419 '#collapsed' => TRUE,
420 );
421 $form['advanced']['title_pattern'] = array(
422 '#type' => 'textfield',
423 '#title' => t('Title pattern'),
424 '#default_value' => $_POST['edit']['title_pattern'],
425 '#maxlength' => 50,
426 '#size' => 30,
427 '#description' => t('If you leave this blank, each imported file\'s title will be set to match the filename. If you wish, you can specify an alternative title pattern here that will be used as a prefix (e.g., a title pattern of &quot;my title:&quot; will label a file called &quot;photo-0032.jpg&quot; as &quot;my title: photo-0032.jpg&quot;; the import software adds a space if needed between the prefix and the filename). You can embed the label %f within the pattern to specify exactly where you want the filename to go; if you do that, be sure to add your own space(s) where you want them, because the software will create the titles exactly as you specify. And you can embed %c within the pattern; this will be replaced with the first line of the caption file (if found), allowing you to title the image based on its caption file. If this substitution is made, the first line is removed from the caption file after it is extracted for the title.'),
428 );
429 if (user_access('override image import path')) {
430 $form['advanced']['import_path'] = array(
431 '#type' => 'textfield',
432 '#title' => t('Import path'),
433 '#default_value' => image_import_get_import_path(FALSE),
434 '#maxlength' => 50,
435 '#size' => 30,
436 '#description' => t('You can optionally override the directory from which image files will be imported. This is relative to the site\'s top-level directory. The default value is "%path".', array('%path'=>image_import_get_import_path(FALSE))),
437 );
438 }
439 $form['advanced']['adjust_order'] = array(
440 '#type' => 'checkbox',
441 '#title' => t('Adjust timestamp order of images'),
442 '#default_value' => $adjust_order,
443 '#return_value' => 1,
444 '#description' => t('By default, imported images are marked as created at the actual time they are imported. Since images are imported in filename order, this may cause some albums or galleries to appear in reverse order. Checking this option causes the import to apply a reversed-order timestamp to correct this, though that means the displayed creation timewill differ by a few seconds from the actual creation time.'),
445 );
446 if ($user->uid == 1 || ! variable_get('image_import_force_delete',FALSE)) {
447 $form['delete_after'] = array(
448 '#type' => 'checkbox',
449 '#title' => t('Delete uploaded image files after import'),
450 '#default_value' => $delete_after,
451 '#return_value' => 1,
452 '#description' => t('This option causes the import program to delete the images and any caption files from the import directory after they are imported into the gallery.'),
453 );
454 }
455 $form['submit_preview'] = array(
456 '#type' => 'submit',
457 '#value' => t('Preview import'),
458 );
459 $form['submit_import'] = array(
460 '#type' => 'submit',
461 '#value' => t('Import images'),
462 );
463
464 /* added the following to trick taxonomy_form_alter into adding the categories form element */
465 $node = new stdclass();
466 $node->type = 'image';
467 $form['#node'] = $node;
468 $form['type'] = array('#value' => 'image');
469 /* end of trick */
470
471 // changed the form_id to contain the fragment _node_form; also needed to trick taxonomy_form_alter
472 $html .= drupal_get_form('image_node_form', $form);
473 return $html;
474 }
475
476 /**
477 * From a list of all extensions this module recognizes as image files, returns
478 * an array containing only those permitted by system settings for the upload
479 * module.
480 */
481 function _image_import_img_extensions() {
482 return _image_import_check_ext(array('gif','jpg','jpeg','png','tif','tiff'));
483 }
484
485 /**
486 * From a list of all extensions this module recognizes as caption files, returns
487 * an array containing only those permitted by system settings for the upload
488 * module.
489 */
490 function _image_import_cap_extensions() {
491 return _image_import_check_ext(array('html','htm','txt','caption'));
492 }
493
494 /**
495 * Given an array of extensions allowed by this module specifically, intersects
496 * these with the system-defined list of permitted upload extensions in order
497 * to create a subset of only the ones legal in both contexts. The parameter is
498 * a simple array whose values are significant but whose keys are not. The return
499 * is a simple integer-subscripted array.
500 *
501 * The lists of extensions for different security roles are stored in variables
502 * whose names are "upload_extensions_${rid}". This function merges all of the
503 * extensions for the user's roles, then intersects the $ext_array with the
504 * result of that merge.
505 */
506 function _image_import_check_ext($ext_array) {
507 global $user;
508 // Special handling for the superuser
509 if ($user->uid == 1) {
510 return $ext_array;
511 }
512 $extensions = array();
513 foreach($user->roles as $rid=>$role) {
514 $rid = intval($rid);
515 $role_extensions = explode(' ',variable_get('upload_extensions_'.$rid, ''));
516 $extensions = array_merge($extensions, $role_extensions);
517 }
518 $extensions = array_values(array_intersect($ext_array, $extensions));
519 return $extensions;
520 }
521
522 /**
523 * Calculates the largest upload size for the current user, by finding the
524 * maximum of variables named "upload_uploadsize_${rid}" and the system
525 * upload limit.
526 */
527 function _image_import_max_upload_size() {
528 global $user;
529 $size = 0;
530 foreach($user->roles as $rid=>$role) {
531 $rid = intval($rid);
532 $role_size = intval(variable_get('upload_uploadsize_'.$rid, 0));
533 $size = max($size, $role_size);
534 }
535 $size = min(intval(variable_get('upload_maxsize_total',99999999)), $size);
536 return $size;
537 }
538
539 /**
540 * Processes, or pre-checks, the import of the file(s) in the import directory.
541 * Returns an associative array (keyed by basename) of the images that would
542 * be imported (precheck mode) or for which import was attempted.
543 *
544 * Returns one of the status code constants.
545 */
546 function image_import_import($precheck_only=TRUE) {
547 global $user;
548 $debug = variable_get('image_import_debug', 0);
549 if ($debug) drupal_set_message(t('DEBUG MODE ENABLED for image_import_import() function. There will be extra messages issued.'),'debug');
550
551 $start = time();
552 $timeout = intval(variable_get('image_import_timeout', 0));
553 $adjust_order = $_POST['edit']['adjust_order'];
554 if ($debug && $adjust_order) drupal_set_message(t('DEBUG: Throttle is enabled.'),'debug');
555 $is_personal = ($_POST['edit']['personal'] || ! user_access('import system images'));
556 $extensions = _image_import_img_extensions();
557 $cap_extensions = _image_import_cap_extensions();
558 if ($is_personal) {
559 drupal_set_message(t('Personal albums not yet supported; importing to system galleries.'),'warning');
560 }
561 if ($user->uid == 1 || ! variable_get('image_import_force_delete',FALSE)) {
562 $do_delete = $_POST['edit']['delete_after'];
563 } else {
564 $do_delete = variable_get('image_import_force_delete',FALSE);
565 }
566 if (! $do_delete && $timeout > 0) {
567 drupal_set_message(t('Normal %time second time limit will be ignored because files are not being deleted after import.', array('%time'=>$timeout)), 'warning');
568 $timeout = 0;
569 }
570 $title_pattern = $_POST['edit']['title_pattern'];
571 // If the user specified an exact pattern, take it on faith that they know what they're doing.
572 // Otherwise, make sure it ends with a blank and then append %f so that the actual import
573 // has a place to put the filename.
574 if (! preg_match('/%[fc]/',$title_pattern)) {
575 $title_pattern .= ' %f';
576 $title_pattern = preg_replace('/ /',' ',$title_pattern,-1);
577 }
578 // No matter what, no leading or trailing blanks
579 $title_pattern = trim($title_pattern);
580 if (is_array($extensions)) {
581 $img_ext = array();
582 foreach ($extensions as $extension) {
583 $lc = strtolower($extension);
584 $uc = strtoupper($extension);
585 $img_ext[] = $lc;
586 $img_ext[] = $uc;
587 }
588 if ($debug) drupal_set_message(t('DEBUG: Image file extensions: %ext',array('%ext'=>implode(',',$img_ext))),'debug');
589 } else {
590 drupal_set_message(t('Internal error: invalid file extension list in image_import_import()'),'error');
591 return IMAGE_IMPORT_STATUS_ABORT;
592 }
593 $path = image_import_get_import_path(TRUE);
594 $img_mask = '\.(' . implode('|',$img_ext) . ')$';
595 $files = file_scan_directory($path, $img_mask, array('.', '..', 'CVS'), 0, FALSE, 'name');
596 if (! count($files)) {
597 drupal_set_message(t('No image files were found in the import directory %p.',array('%p'=>$path)), 'warning');
598 } else {
599 // The directory scan doesn't necessarily return the files in a reasonable order
600 ksort($files);
601 $found_count = 0;
602 $captioned_count = 0;
603 $imported_count = 0;
604 $error_count = 0;
605 if (is_array($cap_extensions)) {
606 $cap_ext = array();
607 foreach ($cap_extensions as $extension) {
608 $lc = strtolower($extension);
609 $uc = strtoupper($extension);
610 $cap_ext[] = $lc;
611 $cap_ext[] = $uc;
612 }
613 if ($debug) drupal_set_message(t('DEBUG: Caption file extensions: %ext',array('%ext'=>implode(',',$cap_ext))),'debug');
614 } else {
615 drupal_set_message(t('Internal error: invalid caption extension list in image_import_import()'),'error');
616 return IMAGE_IMPORT_STATUS_ABORT;
617 }
618 $terms =& _image_import_extract_tids($_POST['edit']['taxonomy']);
619
620 if (! is_array($terms) || count($terms) < 1) {
621 drupal_set_message(t('No categories selected for images; aborting.'),'error');
622 return IMAGE_IMPORT_STATUS_ABORT;
623 }
624 $last = time() + count($files);
625 $nfile = 0;
626 foreach($files as $file) {
627 $nfile++;
628 // Do the files in reverse time order so they'll sort sensibly
629 $now = $last - $nfile;
630 if ($debug) drupal_set_message(t("DEBUG: Timestamp for file number %n is %t", array('%n'=>$nfile, '%t'=>$now)), 'debug');
631 $elapsed = time() - $start;
632 if ($timeout > 0 && $elapsed >= $timeout) {
633 drupal_set_message(t('Time limit expired. One or more images remain to be imported. Repeat the import to continue processing these images.'),'warning');
634 return IMAGE_IMPORT_STATUS_OK_TIMEOUT;
635 }
636 $found_count++;
637 // Import or check ONE file only
638 $rc = image_import_import_one($file, $cap_ext, $precheck_only, $title_pattern, $do_delete, $terms, ($adjust_order ? $now : 0));
639 switch ($rc) {
640 case IMAGE_IMPORT_STATUS_OK_CAPTION:
641 $captioned_count++;
642 // Intentional fall-through here.
643 case IMAGE_IMPORT_STATUS_OK:
644 $imported_count++;
645 if ($do_delete && ! $precheck_only) $deleted_count++;
646 break;
647 case IMAGE_IMPORT_STATUS_ABORT:
648 drupal_set_message(t('Fatal error. Import processing aborted.'), 'error');
649 break 2;
650 case IMAGE_IMPORT_STATUS_ERROR:
651 $error_count++;
652 break;
653 default:
654 if ($rc < 0) {
655 $error_count++;
656 } else {
657 $imported_count++;
658 if ($do_delete && ! $precheck_only) $deleted_count++;
659 }
660 }
661 }
662 if ($precheck_only) {
663 drupal_set_message(t('Found %n file(s) to import, with %c captioned from files.',array('%n'=>$found_count,'%c'=>$captioned_count)));
664 } else {
665 drupal_set_message(t('Successfully imported %i file(s) out of %n, with %c captioned from files.', array('%i'=>$imported_count, '%n'=>$found_count, '%c'=>$captioned_count)));
666 watchdog('image import',t('Successfully imported %i file(s) out of %n, with %c captioned from files.', array('%i'=>$imported_count, '%n'=>$found_count, '%c'=>$captioned_count)), WATCHDOG_NOTICE);
667 }
668 if ($deleted_count) {
669 drupal_set_message(t('Deleted %d image(s) and associated caption(s) from the import directory.', array('%d'=>$deleted_count)));
670 }
671 }
672 return IMAGE_IMPORT_STATUS_OK;
673 }
674
675 /**
676 * Attempt to import, or just to check, one file.
677 *
678 * Returns one of the following status values:
679 * IMAGE_IMPORT_STATUS_ABORT The attempt was aborted due to an internal
680 * error, and a message has been queued to the user.
681 * IMAGE_IMPORT_STATUS_OK The attempt succeeded and the image was imported
682 * (or would be expected to import okay).
683 * IMAGE_IMPORT_STATUS_OK_CAPTION The attempt succeeded (or is expected to succeed)
684 * and a caption file was found for the image.
685 * IMAGE_IMPORT_STATUS_ERROR The image cannot be imported; a message has been
686 * queued to report the specific problem.
687 * Other status codes may be added in the future. The application may safely assume
688 * that any code >= 0 is some form of success, and any code < 0 is some form of failure.
689 */
690 function image_import_import_one($file, $cap_ext, $precheck_only=TRUE, $title_pattern='%f', $do_delete=FALSE, $terms=NULL, $timestamp=0) {
691 global $user;
692 $debug = variable_get('image_import_debug', 0);
693 if (! is_object($file)) {
694 drupal_set_message(t('Import function called with null file; aborting.'),'error');
695 return IMAGE_IMPORT_STATUS_ABORT;
696 }
697 if (! is_array($cap_ext)) {
698 drupal_set_message(t('Import function called with empty caption list; aborting.'),'error');
699 return IMAGE_IMPORT_STATUS_ABORT;
700 }
701 if (! is_array($terms) || count($terms) < 1) {
702 drupal_set_message(t('No categories selected for images; aborting.'),'error');
703 return IMAGE_IMPORT_STATUS_ABORT;
704 }
705 if (! $timestamp) {
706 $timestamp = time();
707 }
708 $cap_mask = '\.(' . implode('|',$cap_ext) . ')$';
709 // Double-slashes in paths don't seem to matter to the o.s., but they
710 // confuse the user in error messages, so we get rid of them here.
711 $path = preg_replace('#//#','/',$file->filename,-1);
712 if ($debug) drupal_set_message(t('DEBUG: Import called for path=%path',array('%path'=>$path)),'debug');
713 // Access checks on the file
714 if (! is_readable($path)) {
715 drupal_set_message(t('The web server cannot read %f -- skipping.', array('%f'=>$path)),'error');
716 return IMAGE_IMPORT_STATUS_ERROR;
717 }
718 if (! is_writable($path)) {
719 drupal_set_message(t('Image file %f is not writable by the web server -- skipping.', array('%f'=>$path)),'error');
720 return IMAGE_IMPORT_STATUS_ERROR;
721 }
722 if ($debug) drupal_set_message(t('DEBUG: Access checks succeeded for %path',array('%path'=>$path)),'debug');
723 $caption = '';
724 $cap_extensions = _image_import_cap_extensions();
725 $cap_firstline = '';
726 foreach ($cap_extensions as $ext) {
727 $caption_path = preg_replace('#\.[^.]+$#','.'.$ext, $path);
728 if (file_exists($caption_path)) {
729 if ($debug) drupal_set_message(t('DEBUG: Found caption file %file',array('%file'=>$caption_path)),'debug');
730 if (! is_readable($caption_path)) {
731 drupal_set_message(t('The web server cannot read %f -- skipping.', array('%f'=>$caption_path)),'error');
732 return IMAGE_IMPORT_STATUS_ERROR;
733 }
734 if ($do_delete && ! is_writable($caption_path)) {
735 drupal_set_message(t('Delete of imported files is enabled, but caption file %f is not writable by the web server -- skipping image.', array('%f'=>$caption_path)),'error');
736 return IMAGE_IMPORT_STATUS_ERROR;
737 }
738 $caption = file_get_contents($caption_path, FALSE);
739 $caption = preg_replace('#\r\n#m',"\n",$caption,-1);
740 $caption = preg_replace('#\r#m',"\n",$caption,-1);
741 if (strpos($title_pattern, '%c') !== FALSE) {
742 $lines = explode("\n", $caption);
743 $cap_firstline = trim(strip_tags($lines[0]));
744 if (! empty($cap_firstline)) {
745 unset($lines[0]);
746 $caption = implode("\n", $lines);
747 }
748 }
749 $caption = '<p>' . $caption;
750 $caption = preg_replace('#\n\s*\n#m',"\n<p>\n",$caption,-1);
751 $caption = preg_replace('#<p>\s*<p>#m','<p>',$caption,-1);
752 break;
753 }
754 }
755 $title = trim(preg_replace('#%f#',$file->basename,$title_pattern));
756 $title = trim(preg_replace('#%c#',$cap_firstline,$title));
757 if ($debug) drupal_set_message(t('DEBUG: Title pattern is "%p".', array('%p'=>$title_pattern)), 'debug');
758 if ($debug) drupal_set_message(t('DEBUG: Title is "%t".', array('%t'=>$title)), 'debug');
759 $files[$name]->title = $title;
760 $teaser = node_teaser($caption);
761 if ($debug) drupal_set_message(t('DEBUG: Caption %c characters, teaser %t characters, for %n.', array('%c'=>strlen($caption), '%t'=>strlen($teaser), '%n'=>$file->basename)),'debug');
762
763 $nodeoptions = variable_get('node_options_image_import','');
764 if (! is_array($nodeoptions)) {
765 drupal_set_message(t('Node workflow options have not been %configured for the image_import type. Defaults will be used.', array('%configured'=>l(t('configured'),'admin/node/configure/types/image_import'))), 'warning');
766 $nodeoptions = array();
767 }
768
769 // ****** All prechecks are completed here *******
770 if ($precheck_only || $debug) {
771 drupal_set_message(($debug ? t('DEBUG: ') : '') . t('Image %n prechecks okay and should import successfully.', array('%n'=>$file->basename)), $debug ? 'debug' : 'status');
772 }
773 if ($precheck_only) {
774 return (empty($caption) ? IMAGE_IMPORT_STATUS_OK : IMAGE_IMPORT_STATUS_OK_CAPTION);
775 }
776
777 // ****** The following executes only if we are doing a real import ******
778
779 $node =& new stdClass();
780 $node->type = IMAGE_IMPORT_IMAGE_NODETYPE;
781 $node->uid = $user->uid;
782 $node->title = $title;
783 $node->body = $caption;
784 $node->teaser = $teaser;
785 $node->created = $timestamp;
786 $node->changed = $timestamp;
787 $node->promote = in_array('promote', $nodeoptions) ? 1 : 0;
788 $node->status = in_array('status', $nodeoptions) ? 1 : 0;
789 $node->sticky = in_array('sticky', $nodeoptions) ? 1 : 0;
790 $node->revision = in_array('revision', $nodeoptions) ? 1 : 0;
791 $node->moderate = in_array('moderate', $nodeoptions) ? 1 : 0;
792 $node->comment = variable_get('comment_image_import',0);
793
794 if (! image_import_do_node($node, $path)) {
795 return IMAGE_IMPORT_STATUS_ERROR;
796 }
797
798 $nid = $node->nid;
799
800 taxonomy_node_save($nid, $terms);
801 if ($debug) drupal_set_message(t('DEBUG: Saved taxonomy for node nid=%nid',array('%nid'=>$nid)),'debug');
802 _image_build_derivatives($node, FALSE);
803 if ($debug) drupal_set_message(t('DEBUG: Built derivatives for node nid=%nid',array('%nid'=>$nid)),'debug');
804
805 if ($do_delete) {
806 $cap_ok = file_exists($caption_path) ? file_delete($caption_path) : FALSE;
807 if ($cap_ok) drupal_set_message(t('Deleted caption file %p',array('%p'=>$caption_path)),'status');
808 $img_ok = file_exists($path) ? file_delete($path) : FALSE;
809 if ($img_ok) drupal_set_message(t('Deleted image file %p',array('%p'=>$path)),'status');
810 }
811
812 if (empty($caption)) {
813 return IMAGE_IMPORT_STATUS_OK;
814 } else {
815 return IMAGE_IMPORT_STATUS_OK_CAPTION;
816 }
817 }
818
819 /**
820 * Perform the bare image import process on a predefined (existing or new)
821 * node object. All this function does is to physically the file from its
822 * current location into the image directory, then save the node object
823 * using the Drupal node API.
824 *
825 * $path is the Drupal path to the image file in the import directory.
826 */
827 function image_import_do_node(&$node, $path) {
828 $debug = variable_get('image_import_debug', 0);
829
830 $image_dir = _image_import_get_image_path();
831 if ($debug) drupal_set_message(t('DEBUG: System image directory is \'%id\'.', array('%id'=>$image_dir)), 'debug');
832
833 // Need this dummy array because node_save() calls image_insert() since
834 // this is a node of type 'image'. In this situation, however, we will
835 // take care of importing the file for this node separately.
836 $node->images = array();
837
838 // Copy the image file into the appropriate directory
839 $new_path = $path;
840 if ($debug) drupal_set_message(t('DEBUG: Invoking file_copy(\'%path\', \'%dir\', FILE_EXISTS_RENAME)', array('%path'=>$new_path,'%dir'=>$image_dir)), 'debug');
841 if (file_copy($new_path, $image_dir, FILE_EXISTS_RENAME)) {
842 // The $tmp_path receives the destination path
843 if ($debug) drupal_set_message(t('DEBUG: Copied %p to %np.', array('%p'=>$path, '%np'=>$new_path)), 'debug');
844 } else {
845 drupal_set_message(t('File copy failed for %p to %tp', array('%p'=>$path, '%tp'=>$tmp_path)), 'error');
846 return FALSE;
847 }
848 $node->file = $node->images['_original'] = $new_path;
849
850 // saving a node - first step is to validate
851 $node = node_submit($node);
852 if ($node->validated) {
853 // ... then we save the node
854 node_save($node);
855 if ($debug) drupal_set_message(t('DEBUG: Saved node nid=%nid',array('%nid'=>$nid)),'debug');
856 watchdog('image import',t('Imported "%p" as node %nid',array('%p'=>$path, '%nid'=>$nid)), WATCHDOG_NOTICE, l(t('view'),"node/$nid"));
857 } else {
858 drupal_set_message(t('Unable to create image node.'),'error');
859 return FALSE;
860 }
861 return TRUE;
862 }
863
864 /**
865 * Attempt to fully process an input image file, creating all resized
866 * versions and storing it into the {files} database table.
867 */
868 function &_image_import_get_file_object($filepath) {
869 $info = image_get_info($filepath);
870 $file = new StdClass();
871 $file->filename = trim(basename($filepath), '.');
872 $file->filemime = $info['mime_type'];
873 $file->filepath = $filepath;
874 $file->error = '';
875 $file->filesize = filesize($filepath);
876 return $file;
877 }
878
879 /**
880 * Looks up the current default path for images, from the image.module and
881 * uploads.module settings.
882 */
883 function _image_import_get_image_path() {
884 $imgpath = variable_get('image_default_path','');
885 $filepath = variable_get('file_directory_path','');
886 $path = $filepath . '/' . $imgpath;
887 $path = preg_replace('#//#','',$path,-1);
888 // file_downloads value is 1 for public, 2 for private
889 $dl_type = variable_get('file_downloads','');
890 if ($dl_type<>2) {
891 $path = preg_replace('#^/#','',$path);
892 }
893 $path = preg_replace('#/$#','',$path);
894 return $path;
895 }
896 ?>

  ViewVC Help
Powered by ViewVC 1.1.2