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

Contents of /contributions/modules/monument/monument.module

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


Revision 1.14 - (show annotations) (download) (as text)
Fri Aug 3 22:01:23 2007 UTC (2 years, 3 months ago) by grunthus
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +58 -5 lines
File MIME type: text/x-php
Added capability to show number of associated images in
monument_listbytitle
1 <?php
2 // $Id: monument.module,v 1.13 2007/07/30 23:55:56 grunthus Exp $
3 //define ("_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE",TRUE);
4 define ("_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE",FALSE);
5 //define ("_ASCENT_SOFTWARE_LIMITED_DEBUG2", TRUE);
6 define ("_ASCENT_SOFTWARE_LIMITED_DEBUG2", FALSE);
7
8 /**
9 * @file
10 * Manage data and photos for ancient monuments, especially where a circular
11 * arrangement of photos is useful. Based originally on data relevant to Iron
12 * Age Broch structures found in the north of Scotland.
13 */
14
15 /**
16 * Implementation of hook_node_info().
17 *
18 */
19
20 function monument_node_info() {
21 return array (
22 'monument' => array (
23 'name' => t('Historic Monument'),
24 'module' => 'monument',
25 'description' => t("This module is designed to assist you in recording and " .
26 "presenting details of historic monuments. It works in close concert with " .
27 "the Image module - organising relevant images in a meaningful way is the " .
28 "main goal of the monument module. Once you have created a few monuments, " .
29 "you then submit some images, associating each image with a monument. ".
30 "Of course, a single monument can have many images associated with it, " .
31 "taken from a variety of locations."),
32 ),
33 );
34 }
35
36 /**
37 * Implementation of hook_help().
38 */
39 function monument_help($section) {
40 switch ($section) {
41 case 'admin/help#monument':
42 $output = '<p>' . t("This module assists you in recording and presenting details of historic
43 monuments. It works in close concert with the Image module - organising
44 relevant images in a meaningful way is the main goal of the monument module.
45 Once you have created a monument, you then submit some images, associating
46 each image with the monument. The module then takes care of presenting the
47 images in a meaningful way, hopefully enhancing site visitors' understanding
48 of each monument.") . '</p>';
49 $output .= '<p>' . t("The Image module itself is extended in a number of fairly trivial but important
50 ways. Firstly, each submitted image will also have a 'medium' size version
51 created at 300 pixels wide. This medium image is used to display key images for
52 a monument. Beyond that, there are some additional fields which are recorded for
53 an image:") . '</p>';
54 $output .= '<p>' . t("The Associated Monument must be given first, otherwise the system will
55 just treat the image as an ordinary one, ignoring any other additional data you
56 submit with the image. Since some monuments may have similar names, be sure to
57 pick the right one - the catalogue number can help with that.") . '</p>';
58 $output .= '<p>' . t("Next we have the 'facing bearing' field. When you take a photo of a monument,
59 record the bearing in which the centre of the shot is facing. If you don't know
60 in which direction the centre of the image faces, enter 999 in this field.
61 Currently this information is used to render a 'compass rose' composite image
62 for a particular monument. Other uses are planned in the future.") . '</p>';
63 $output .= '<p>' . t("The date taken field is just that - the date that the photo was taken, or in the
64 case of a drawing or sketch, the date on which it was completed.") . '</p>';
65 $output .= '<p>' . t("The final field 'Image Location' underpins the 'compass rose' composite view of
66 a monument. The system currently recognises six different positional aspects of
67 a monument. An image will be taken from one of these positions. The current
68 module is based on a concentric arrangement of viewing positions as follows:") . '</p>';
69 $output .= '<ul><li>' . t("Ring 1 - taken from the centre of the monument, facing radially outwards, but
70 focussing on the inner details of the remains. Generally photos taken here tend
71 to have a negative elevation angle. (They are looking slightly downward)") . '</li>';
72 $output .= '<li>' . t("Ring 2 - taken facing towards the centre of the monument from a point on a
73 radius external to the outer edge or wall of the monument.") . '</li>';
74 $output .= '<li>' . t("Ring 3 - taken again from the centre of the monument, facing radially outwards,
75 focussing on the horizon view. If the monument itself would obscure the horizon
76 from its own centre, then these shots are taken from the corresponding radial
77 point just exterior to the exterior edge or wall of the monument. Generally
78 photos taken here tend to have a positive elevation angle. (They are looking
79 slightly upwards)") . '</li></ul>';
80 $output .= '<p>' . t("A more advanced co-ordinate system would allow a more flexible approach since
81 not all interesting features fit into a polar co-ordinate model facing radially
82 inward or outward. More generalised co-ordinates are planned for future
83 versions. However in the meantime if none of the 'rings' are appropriate, then
84 you can record an image location as:") . '</p>';
85 $output .= '<ul><li>' . t("ad_hoc - In general position. Describe roughly where you were in the image body.") . '</li>';
86 $output .= '<li>' . t("key_img - Use this image to typify the monument. A key image will be displayed
87 in medium (300px) size.") . '</li>';
88 $output .= '<li>' . t("art_img - Use this if you wish the image to simply be of artistic interest.
89 Perhaps a nearby object or view.") . '</li></ul>';
90 $output .= '<p>' . t("The monument module itself, permits you to record a number of additional
91 catalogue numbers. At present these can only be changed by editing the module's
92 code, however a more generalised approach is planned for future versions.") . '</p>';
93 $output .= '<p>' . t("The OS mapsheet and grid reference is a United Kingdom ordnance survey
94 mapsheet and 4,6 or 8 figure reference. In future versions, some kind of localisation will be needed, perhaps using
95 latitude and longitude along with conversions to local mapping references.") . '</p>';
96 $output .= '<p>' . t("The 'last visited' field records when you last visited the monument. I'm not
97 sure how this could work if a lot of people are able to edit a monument's data.") . '</p>';
98 return $output;
99 }
100 }
101
102
103 /**
104 * Implementation of hook_menu().
105 */
106 function monument_menu($may_cache) {
107 $items = array();
108
109 if ($may_cache) {
110 $items[] = array(
111 'path' => 'node/add/monument',
112 'title' => t('Monument'),
113 'access' => user_access('add monument'),
114 );
115
116
117 } else {
118 $items[] = array(
119 'path' => 'node/monument/rose',
120 'title' => t('Compass Rose'),
121 'callback' => 'theme_monument_advanced_view',
122 'access' => user_access('view monument'),
123 'type' => MENU_CALLBACK
124 );
125
126 $items[] = array(
127 'path' => 'node/monument/rotate',
128 'title' => t('Image Rotation'),
129 'callback' => '_monument_image_rotate',
130 'access' => user_access('view monument'),
131 'type' => MENU_CALLBACK
132 );
133
134 $items[] = array(
135 'path' => 'node/monument/make_compass_rose',
136 'title' => t('Generate the basic rose'),
137 'callback' => '_make_compass_rose',
138 'access' => user_access('view monument'),
139 'type' => MENU_CALLBACK
140 );
141
142 $items[] = array(
143 'path' => 'listbytitle',
144 'title' => t('Monuments by Title'),
145 'callback' => 'monument_listbytitle',
146 'access' => user_access('access content'),
147 'type' => MENU_CALLBACK
148 );
149
150 //TODO this should really be in $may_cache but that doesn't seem to work
151 $items[] = array(
152 'path' => 'admin/settings/monument',
153 'title' => t('Monument'),
154 'description' => t('Place holder for any future monument settings'),
155 //'callback' => 'monument_admin_form',
156 'callback' => 'drupal_get_form',
157 'callback arguments' => array('_monument_admin_form'),
158 'access' => user_access('administer monument'),
159 'type' => MENU_NORMAL_ITEM
160 );
161 }
162 return $items;
163 }
164
165 /**
166 * Implementation of hook_nodeapi().
167 *
168 */
169 function monument_nodeapi(&$node, $op, $teaser, $page) {
170 if ($node->type == 'image') {
171 switch ($op) {
172 // When an image editing form is submitted, we need to re-count
173 // the number of images associated with each monument
174 case 'update':
175 case 'insert':
176 case 'delete':
177 _get_associated_image_counts_by_mid($reset=TRUE);
178 default:
179 //do nothing
180 }
181 }
182 }
183
184
185 /**
186 * Implementation of hook_perm().
187 */
188 function monument_perm() {
189 return array('access monument', 'create monument', 'edit monument', 'edit own monument', 'administer monument');
190 }
191
192 /**
193 * Implementation of hook_access().
194 */
195 function monument_access($op, $node) {
196 global $user;
197
198 if ($op == 'create') {
199 return user_access('create monument');
200 }
201
202 if ($op == 'update' || $op == 'delete') {
203 if (user_access('edit own monument') && ($user->uid == $node->uid)) {
204 return TRUE;
205 } else {
206 if (user_access('edit monument')) {
207 return TRUE;
208 }
209 }
210 }
211
212 if ($op == 'view') {
213 return user_access('access monument');
214 }
215
216 } //function monument_access
217
218
219 /**
220 * Implementation of hook_block().
221 */
222 function monument_block($op = 'list', $delta = 0, $edit = array()) {
223 switch ($op) {
224 case 'list':
225 $block[0]['info'] = t('Latest monument');
226 $block[1]['info'] = t('Random monument');
227 $block[2]['info'] = t('All monuments'); //alphabetical list
228 return $block;
229 case 'view':
230 if (user_access('access content')) {
231 switch($delta) {
232 case 0:
233 $monument = monument_get_latest();
234 $block['subject'] = t('Latest monument');
235 $block['content'] = l(check_plain($monument[0]->title), t('node/@nid', array(
236 '@nid' => $monument[0]->nid)), array(), NULL, NULL, FALSE, FALSE);
237 $body_frag = substr($monument[0]->body,0,60);
238 $block['content'] .= t(": %frag...", array('%frag' => $body_frag));
239 break;
240 case 1:
241 $monument = monument_get_random();
242 $block['subject'] = t('Random monument');
243 $block['content'] = l(check_plain($monument[0]->title), t('node/@nid', array(
244 '@nid' => $monument[0]->nid)), array(), NULL, NULL, FALSE, FALSE);
245 $body_frag = substr($monument[0]->body,0,60);
246 $block['content'] .= t(": %frag...", array('%frag' => $body_frag));
247 break;
248 case 2:
249 $block['subject'] = t('Monuments by title');
250 $block['content'] = _get_monument_letter_links_by_title();
251 break;
252 }
253 }
254 return $block;
255 }
256 }
257
258 /**
259 * Fetch a random N monuments(s) - optionally from a given term.
260 * Code lifted from image module
261 */
262 function monument_get_random($tid = 0, $count=1) {
263 if ($tid != 0) {
264 $result = db_query_range(
265 "SELECT n.nid FROM {term_node} tn" .
266 " LEFT JOIN {node} n ON n.nid=tn.nid WHERE n.type='monument' " .
267 "AND n.status=1 AND tn.tid=%d ORDER BY RAND()", $tid, 0, $count
268 );
269 } else {
270 $result = db_query_range(
271 "SELECT n.nid FROM {node} n " .
272 "WHERE n.type='monument' AND n.status=1 ORDER BY RAND()", 0, $count
273 );
274 }
275 $output = array();
276 while ($nid = db_fetch_object($result)) {
277 $output[] = node_load(array('nid' => $nid->nid));
278 }
279 if(_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) {
280 echo("<br />Random:");print_r($output);
281 }
282 return $output;
283 }
284
285 /**
286 * Fetch the latest N image(s) - optionally from a given term.
287 * Code lifted from image module
288 */
289 function monument_get_latest($count = 1, $tid = 0) {
290 if ($tid != 0) {
291 $result = db_query_range("SELECT n.nid FROM {term_node} tn" .
292 " LEFT JOIN {node} n ON n.nid=tn.nid WHERE n.type='monument' " .
293 "AND n.status=1 AND tn.tid=%d ORDER BY n.changed DESC",
294 $tid, 0, $count);
295 }
296 else {
297 $result = db_query_range("SELECT n.nid FROM {node} n " .
298 "WHERE n.type='monument' AND n.status=1 ORDER BY changed DESC",
299 0, $count);
300 }
301 $output = array();
302 while ($nid = db_fetch_object($result)) {
303 $output[] = node_load(array('nid' => $nid->nid));
304 }
305 return $output;
306 }
307
308
309 /**
310 * Implementation of hook_delete().
311 */
312 function monument_delete(&$node) {
313 db_query(
314 'DELETE FROM {monument} ' .
315 'WHERE nid = %d', $node->nid
316 );
317 }
318
319
320 /**
321 * Implementation of hook_form().
322 */
323 function monument_form(&$node, &$param) {
324
325 $type = node_get_types('type', $node);
326
327 $form['title'] = array(
328 '#type' => 'textfield',
329 '#title' => check_plain($type->title_label),
330 '#description' => t('Name of this Monument'),
331 '#required' => TRUE,
332 '#default_value' => check_plain($node->title),
333 '#weight' => -10
334 );
335
336 $form['alt_names'] = array(
337 '#type' => 'textfield',
338 '#title' => t('Alternative names'),
339 '#description' => t('Provide any alternate names for this monument in a' .
340 ' comma separated list'),
341 '#required' => FALSE,
342 '#default_value' => check_plain($node->alt_names),
343 '#weight' => -9,
344 );
345
346 $form['catalogue'] = array(
347 '#type' => 'textfield',
348 '#title' => t('Catalogue number'),
349 '#required' => TRUE,
350 '#default_value' => check_plain($node->catalogue),
351 '#weight' => -8,
352 '#size' => 3,
353 '#maxlength' => 3,
354 '#description' => t('RockStanza catalogue number')
355 );
356
357 $form['SAT_PRN'] = array(
358 '#type' => 'textfield',
359 '#title' => t('SAT\'s PRN'),
360 '#required' => FALSE,
361 '#default_value' => check_plain($node->SAT_PRN),
362 '#weight' => -7,
363 '#description' => t('Shetland Amenity Trust PRN')
364 );
365
366 $form['RCAMS_orig'] = array(
367 '#type' => 'textfield',
368 '#title' => t('RCAMS original'),
369 '#required' => FALSE,
370 '#default_value' => check_plain($node->RCAMS_orig),
371 '#weight' => -6,
372 '#description' => t('Original RCAMS survey number')
373 );
374
375 $form['RCAHMS_NMR'] = array(
376 '#type' => 'textfield',
377 '#title' => t('RCAHMS NMR'),
378 '#required' => FALSE,
379 '#default_value' => check_plain($node->RCAHMS_NMR),
380 '#weight' => -5,
381 '#description' => t('RCAHMS NMR')
382 );
383
384 $form['HS_monnum'] = array(
385 '#type' => 'textfield',
386 '#title' => t('Historic Scotland'),
387 '#required' => FALSE,
388 '#default_value' => check_plain($node->HS_monnum),
389 '#weight' => -5,
390 '#description' => t('Historic Scotland monument number')
391 );
392
393 $form['district'] = array(
394 '#type' => 'textfield',
395 '#title' => t('District'),
396 '#required' => FALSE,
397 '#default_value' => check_plain($node->district),
398 '#weight' => -4,
399 '#description' => t('District where Monument is located')
400 );
401
402 $form['mapsheet'] = array(
403 '#type' => 'textfield',
404 '#title' => t('OS Mapsheet'),
405 '#required' => FALSE,
406 '#default_value' => check_plain($node->mapsheet),
407 '#weight' => -3,
408 '#description' => t('Ordnance Survey mapsheet')
409 );
410
411 $form['gridref'] = array(
412 '#type' => 'textfield',
413 '#title' => t('Grid reference'),
414 '#required' => FALSE,
415 '#default_value' => check_plain($node->gridref),
416 '#weight' => -2,
417 '#description' => t('6 or 8 figure grid reference')
418 );
419
420 $form['original_diameter_metre'] = array(
421 '#type' => 'textfield',
422 '#title' => t('Original diameter /metre'),
423 '#required' => FALSE,
424 '#default_value' => check_plain($node->original_diameter_metre),
425 '#weight' => -1,
426 '#description' => t('Estimated external original diameter / metres ')
427 );
428
429 $form['last_visited'] = array(
430 '#type' => 'textfield',
431 //'#attributes' => array('class' => 'jscalendar'), //adjust format dd/mm/yyyy
432 '#title' => t('Last visited'),
433 '#required' => FALSE,
434 '#default_value' => check_plain($node->last_visited),
435 '#weight' => 0,
436 '#description' => t('Date when this site was last visited ' .
437 'by a site member')
438 );
439
440 $form['body_filter']['body'] = array(
441 '#type' => 'textarea',
442 '#title' => check_plain($type->body_label),
443 '#required' => FALSE,
444 '#default_value' => filter_xss($node->body),
445 '#weight' => 1,
446 '#description' => t('Detailed description of monument')
447 );
448 $form['body_filter']['format'] = filter_form($node->format);
449
450 return $form;
451 }
452
453
454 /**
455 * Implementation of hook_validate().
456 */
457 function monument_validate($form, $values) {
458 //Date check syntax lifted from drupal website http://drupal.org/node/71959
459 // if the user specified a date, validate the format
460 if ( ($values['last_visited']['#value']!=NULL)
461 && (trim($values['last_visited']['#value']) != '')) {
462 // check the date format
463 if (!preg_match( '/^(\d\d)\/(\d\d)\/(\d\d\d\d)$/',
464 $values['last_visited']['#value'],
465 $matches)) {
466 form_set_error( 'last_visited',
467 t('Date last visited should be in the ' .
468 'format dd/mm/yyyy'));
469 } else {
470 //make sure the date provided is a valid date
471 if (!checkdate($matches[2], $matches[1], $matches[3])) {
472 form_set_error('last_visited', t('Please enter a valid date'));
473 }
474 }
475 }
476
477 if ( ($values['SAT_PRN']['#value']!=NULL)
478 && (!is_numeric($values['SAT_PRN']['#value']))) {
479 form_set_error('SAT_PRN', t('The SAT\'s PRN should be a whole number'));
480 }
481
482 if ( ($values['RCAMS_orig']['#value']!=NULL)
483 && !((string)$values['RCAMS_orig']['#value']
484 === (string)(int)$values['RCAMS_orig']['#value'])) {
485 form_set_error('RCAMS_orig', t('The RCAMS number from 1947' .
486 ' should be a whole number'));
487 }
488
489 if ( ($values['mapsheet']['#value']!=NULL)
490 && (is_numeric($values['mapsheet']['#value']))
491 || (strlen($values['mapsheet']['#value'])!=2)) {
492 form_set_error( 'mapsheet',
493 t('The OS mapsheet should be a two-letter code'));
494 }
495
496 if ( ($values['original_diameter_metre']['#value']!=NULL)
497 && (!is_numeric($values['original_diameter_metre']['#value']))) {
498 form_set_error( 'original_diameter_metre',
499 t('The monument diameter should be a number ' .
500 '(in metres, but don\'t write the unit in)'));
501 }
502
503 if ( ($values['gridref']['#value']!=NULL)
504 && (!((string)$values['gridref']['#value']
505 === (string)(int)$values['gridref']['#value']))) {
506 form_set_error('gridref', t('Grid reference should just be numbers!'));
507 }
508
509 if ( ($values['gridref']['#value']!=NULL)
510 && ((strlen($values['gridref']['#value'])!=4)
511 && (strlen($values['gridref']['#value'])!=6)
512 && (strlen($values['gridref']['#value'])!=8))) {
513 form_set_error( 'gridref',
514 t('Please quote either a 4,6 or 8 figure grid-reference'));
515 }
516 if ( ($values['catalogue']['#value']!=NULL)
517 && (!((string)$values['catalogue']['#value']
518 === (string)(int)$values['catalogue']['#value'])
519 || (strlen($values['catalogue']['#value'])!=3))) {
520 form_set_error( 'catalogue',
521 t('The monument catalogue number should ' .
522 'be a 3 digit number'));
523 }
524 }
525
526
527 /**
528 * Implementation of hook_insert().
529 */
530 function monument_insert($node) {
531 if ($node->last_visited==NULL) {
532 $ts='';
533 } else {
534 //date syntax from http://drupal.org/node/71961
535 preg_match('/^(\d\d)\/(\d\d)\/(\d\d\d\d)$/', $node->last_visited, $m);
536 $ts = mktime(0, 0, 0, $m[2], $m[1], $m[3]);
537 }
538 db_query("INSERT INTO {monument} (nid, catalogue, alt_names, SAT_PRN, " .
539 "RCAMS_orig, RCAHMS_NMR, HS_monnum, district, mapsheet, gridref, " .
540 "original_diameter_metre, last_visited) " .
541 "VALUES (%d, %d, '%s', %d, %d, '%s', %d, '%s', '%s', %d, %d, %d)",
542 $node->nid, $node->catalogue, $node->alt_names,
543 $node->SAT_PRN, $node->RCAMS_orig, $node->RCAHMS_NMR, $node->HS_monnum,
544 $node->district, strtoupper($node->mapsheet), $node->gridref,
545 $node->original_diameter_metre, $ts);
546 }
547
548
549 /**
550 * Implementation of hook_load().
551 */
552 function monument_load($node) {
553 $t = db_fetch_object(db_query("SELECT monumentID, catalogue, alt_names, SAT_PRN, " .
554 "RCAMS_orig, RCAHMS_NMR, HS_monnum, district, mapsheet, gridref, " .
555 "original_diameter_metre, last_visited " .
556 "FROM {monument} WHERE {monument}.{nid} = %d", $node->nid));
557 return $t;
558 }
559
560
561 /**
562 * Implementation of hook_prepare
563 * @param node object to display
564 */
565 function monument_prepare(&$node) {
566 //ensure that NULL last_visited date is not interpreted as a date during edit!
567 if ($node->last_visited) {
568 $node->last_visited = date('d/m/Y', check_plain($node->last_visited));
569 } else {
570 $node->last_visited = "";
571 }
572 //ensure that any other null fields are not re-interpreted as 0 during edit
573 if (!$node->alt_names) $node->alt_names = "";
574 if (!$node->SAT_PRN) $node->SAT_PRN = "";
575 if (!$node->RCAHMS_NMR) $node->RCAHMS_NMR = "";
576 if (!$node->RCAMS_orig) $node->RCAMS_orig = "";
577 if (!$node->district) $node->district = "";
578 if (!$node->original_diameter_metre) $node->original_diameter_metre = "";
579 if (!$node->gridref) $node->gridref = "";
580 if (!$node->HS_monnum) $node->HS_monnum = "";
581 }
582
583 /**
584 * Implementation of hook_update().
585 */
586 function monument_update($node) {
587 if ($node->revision) {
588 monument_insert($node);
589 }
590 else {
591 if ($node->last_visited==NULL) {
592 $ts='';
593 } else {
594 //date syntax from http://drupal.org/node/71961
595 preg_match('/^(\d\d)\/(\d\d)\/(\d\d\d\d)$/', $node->last_visited, $m);
596 $ts = mktime(0, 0, 0, $m[2], $m[1], $m[3]);
597 }
598 db_query("UPDATE {monument} SET catalogue=%d, alt_names='%s', SAT_PRN=%d, " .
599 "RCAMS_orig=%d, RCAHMS_NMR='%s', HS_monnum=%d, district='%s', " .
600 "mapsheet='%s', gridref=%d, original_diameter_metre=%d, " .
601 "last_visited=%d WHERE nid=%d",
602 $node->catalogue, $node->alt_names, $node->SAT_PRN, $node->RCAMS_orig,
603 $node->RCAHMS_NMR, $node->HS_monnum, $node->district,
604 strtoupper($node->mapsheet), $node->gridref,
605 $node->original_diameter_metre, $ts, $node->nid);
606 }
607 }
608
609 /**
610 * Function to list all monuments with title beginning with
611 * specified first letter.
612 */
613 function monument_listbytitle($first_letter) {
614 $fl = '^'.$first_letter[0];
615
616 $page_content = _get_monument_letter_links_by_title();
617 $page_content .= "<hr /><br /><strong>$first_letter[0]</strong><br />";
618 //get all monuments with chosen first letter of title
619 $result = db_query(
620 "SELECT {node}.nid, monumentID, catalogue, title, SAT_PRN, district " .
621 "FROM {monument} INNER JOIN {node} " .
622 "ON {monument}.{nid}={node}.{nid} " .
623 "WHERE {node}.{type} = 'monument' " .
624 "AND {node}.{title} REGEXP '%s' " .
625 "ORDER BY {title} ASC", $fl);
626
627 $page_content .= "<ul>";
628 //Number of associated images linked to each monument:
629 $image_counts = _get_associated_image_counts_by_mid();
630 //print a summary line for each monument
631 do {
632 $t = db_fetch_array($result);
633 if (!$t) break; //a bit ugly
634 $nid = $t['nid'];
635 $mid = $t['monumentID'];
636 $image_count = $image_counts[$mid]; //the count for this monument
637 $link = l(
638 $t['title'],"node/$nid",
639 array('title' => "Direct link to this monument", 'target' => '_self'),
640 NULL, NULL, FALSE, FALSE);
641 $page_content
642 .= "\n<li>" . $link . " (". $t['district']
643 . "), cat.: " . $t['catalogue'] . ", SAT_PRN: "
644 . $t['SAT_PRN'] . ". Images: " . ($image_count?$image_count:"No") . "</li>";
645 } while ($t);
646 $page_content .= "</ul>";
647 print theme("page", $page_content);
648 }
649
650 /**
651 * Implementation of hook_view().
652 */
653 function monument_view(&$node, $teaser = FALSE, $page = FALSE) {
654 $node = node_prepare($node, $teaser);
655 $node->content['basic_view'] = array(
656 '#value' => theme('monument_basic_view', $node),
657 '#weight' => 1,
658 );
659
660 $node->content['medium_view'] = array(
661 '#value' => theme('monument_medium_view', $node),
662 '#weight' => 2,
663 );
664 return $node;
665 }
666
667 /**
668 * Theme function to display monument node data
669 * @param node to display
670 * @return HTML string with additional node information
671 */
672 function theme_monument_basic_view($node) {
673 //get the monument ID (mid) from the nid
674 $monument = db_fetch_object(db_query('SELECT monumentID as mid ' .
675 'FROM {monument} WHERE nid = %d', $node->nid));
676 //pull the relevant key monument images belonging to the monument in question
677 $result = db_query('SELECT {monument_image}.nid, img_loc, face_bearing,' .
678 'filepath FROM {monument_image} INNER JOIN {files} ' .
679 'ON {monument_image}.{nid}={files}.{nid} ' .
680 'WHERE {monument_image}.mid = %d ' .
681 'AND {files}.{filename} LIKE \'medium\' ' .
682 'AND img_loc = 5', $monument->mid);
683 $keyimage = db_fetch_array($result);
684
685 //output the basic data
686 $output = '<div class="monument_view_basic">';
687 $nid = $keyimage['nid'];
688 $file = $keyimage['filepath'];
689 $imageurl = file_create_url($file);
690 $bearing = $keyimage['face_bearing'];
691 //show any alternative names
692 if ($node->alt_names) {
693 $output .= t( 'Also known as: %alt_names',
694 array('%alt_names' => $node->alt_names));
695 }
696 //create the key_img as a link to a full size image
697 if ($keyimage) {
698 $img = t("<img src=\"@imageurl\" title=\"file:@file, bearing: @bearing\" />",
699 array('@nid' => $nid, '@bearing' => $bearing, '@file' => $file, '@imageurl' => $imageurl));
700 $path = t("node/@nid", array('@nid' => $nid));
701 $output .= l($img, $path, NULL, NULL, NULL, FALSE, TRUE) . "<br />";
702 } else {
703 $output .= " No key image<br />";
704 }
705 //these elements only displayed if present. Otherwise quietly forget them
706 if ($node->last_visited) {
707 $output .= t( 'Last visited: %visited<br />',
708 array('%visited' => format_date($node->last_visited,
709 'custom', 'd/m/Y')));
710 }
711 $output .= t( 'Catalogue: @catalogue',
712 array('@catalogue' => $node->catalogue));
713 if ($node->SAT_PRN) {
714 $output .= t(', SAT PRN: %SAT_PRN', array('%SAT_PRN' => $node->SAT_PRN));
715 }
716 if ($node->RCAMS_orig) {
717 $output .= t( ', RCAMS: %RCAMS_orig',
718 array('%RCAMS_orig' => $node->RCAMS_orig));
719 }
720 if ($node->RCAHMS_NMR) {
721 $output .= t( ', RCAHMS NMR: %RCAHMS_NMR',
722 array('%RCAHMS_NMR' => $node->RCAHMS_NMR));
723 }
724 if ($node->HS_monnum) {
725 $output .= t( ', HS Monument Number: %HS_monnum',
726 array('%HS_monnum' => $node->HS_monnum));
727 }
728 //there should be a district and map reference data
729 $output .= t( '<br />District: %district. ',
730 array('%district' => $node->district));
731 $maplink = t( '@mapsheet@gridref ',
732 array('@mapsheet' => $node->mapsheet,
733 '@gridref' => $node->gridref));
734 $addr = t( "http://getamap.ordnancesurvey.co.uk/getamap/frames.htm?" .
735 "mapAction=gaz&gazName=g&gazString=@mapsheet@gridref",
736 array( '@mapsheet' => $node->mapsheet,
737 '@gridref' => $node->gridref));
738 $output .= "OS Map-reference: ";
739
740 $output .= l( "$maplink ","$addr",
741 array ( 'title' => "Hint: The map opens in a new window. " .
742 "Instead of closing it, move it to the side and " .
743 "this website will reuse it for any other maps " .
744 "you look at",
745 'onclick' => 'window.open (this.href, \'popupwindow\', \'width=582,height=520,scrollbars=0,resizable=0,menubar=0,taskbar=0\'); return false;'), NULL, NULL, FALSE, FALSE);
746 $output .= "(" . l("from OS website","http://getamap.ordnancesurvey.co.uk",
747 array('title' => "Opens in new window. Firefox users can click " .
748 "wheel for new tab", 'target' => 'externalsite'),
749 NULL, NULL, FALSE, FALSE) .")";
750 $output .= '</div>';
751 return $output;
752 }
753
754
755 /**
756 * Function to display compass rose of monument images
757 * @param node to display
758 * @return HTML string with additional node information
759 */
760 function theme_monument_medium_view($node) {
761 //get the monument ID (mid) from the nid
762 $monument = db_fetch_object(db_query( 'SELECT monumentID as mid ' .
763 'FROM {monument} WHERE nid = %d',
764 $node->nid));
765
766 // join files ON monument_image to get the filenames
767 // and path of all associated images.
768
769 //NB: if the original image is too small, no thumbnail will be made and the query below
770 //will NOT identify the image as associated with the monument.
771 $result = db_query( "SELECT {monument_image}.nid, mid, taken_date, " .
772 "img_loc, face_bearing, filepath " .
773 "FROM {monument_image} INNER JOIN {files} " .
774 "ON {monument_image}.{nid}={files}.{nid} " .
775 "WHERE {monument_image}.mid = %d " .
776 "AND {files}.{filename} LIKE 'thumbnail'",
777 $monument->mid);
778
779 // Count how many images are found
780 // Could use count query but can't be arsed to change it.
781 $count = 0;
782 $images = array();
783
784 if(_ASCENT_SOFTWARE_LIMITED_DEBUG2) { echo "Going into count with mid= $monument->mid\n"; }
785
786 while ($row = db_fetch_array($result)) {
787 //we are going to group the images depending on where they were taken from
788 //this is not needed for the count, but grouping used a little later
789 $iloc = $row['img_loc'];
790 if(_ASCENT_SOFTWARE_LIMITED_DEBUG2) { print_r($iloc);echo"-";print_r($row['nid']);echo" <br />";}
791 //array of img_loc with array of images belonging to each img_loc
792 $images[$iloc][$row['nid']] = $row;
793 $count++;
794 }
795 if(_ASCENT_SOFTWARE_LIMITED_DEBUG2) {print_r($images);echo"<br />end image dump<br />";}
796 //first call the basic_view theme.
797 //$output .= theme_monument_basic_view($node);
798 // next set up a separate div and title for styling by themes if needed
799 $output .= "<div class='monument_view_medium'>";
800 $output .= "<h2 class='title2'>Associated Images</h2>";
801 $output .= t( "There are %count images associated. ",
802 array ('%count' => $count));
803 $nid=$node->nid;
804 //next, output a link to launch a compass rose display of the monument images.
805 $output .= l( "View Compass Rose","node/monument/rose/$nid",
806 array ('title' => "Hint: The compass rose opens in a new" .
807 " window. Instead of closing it, move it to the side" .
808 " and the website will reuse it for any other compass" .
809 " roses you look at", 'onclick' => 'window.open (this.href, \'popupwindow\', \'width=900,height=900,scrollbars=0,resizable=0,menubar=0,taskbar=0\'); return false;'), NULL, NULL, FALSE, FALSE);
810
811 // Next task is to show thumbnails of the images, grouped depending on
812 // where they were taken from.
813 // Loop through the image locations
814 $imglocs = _monument_image_img_locations();
815 for ($iloc=1;$iloc<=6;$iloc++) {
816 $imgloc = $imglocs[$iloc];
817 $output .= t( "<h3>From %imgloc location</h3>", #
818 array ('%imgloc' => $imgloc));
819 // enter a loop - iterate through each image found in current location
820 if (_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) {print_r($images[$iloc]);echo"end images $iloc<br />";}
821 //only process if any images with current img_loc
822 if (sizeof($images[$iloc])) {
823 foreach ($images[$iloc] as $image) {
824 $file = $image['filepath'];
825 $imageurl = file_create_url($file);
826 $bearing = $image['face_bearing'];
827 $nid = $image['nid'];
828 //each thumbnail will be a link to a full image
829 //should be using l() for this
830 $img = t("<img src=\"@imageurl\" title=\"file:@file, bearing: @bearing\" /> ",
831 array('@nid' => $nid, '@bearing' => $bearing, '@file' => $file, '@imageurl' => $imageurl));
832 $path = t("node/@nid", array('@nid' => $nid));
833 $output .= l($img, $path, NULL, NULL, NULL, FALSE, TRUE);
834 }
835 } else $output .= "No images in this location";
836 }
837 $output .= '</div>';
838 return $output;
839 }
840
841
842 /**
843 * Theme function to display monument images in compass rose arrangement
844 * callback for /node/monument/rose
845 * @param node to display
846 * @return HTML string with additional node information
847 */
848 function theme_monument_advanced_view($nid=NULL) {
849 global $base_url;
850 $output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN">' . "\n";
851 $output .= "<html>\n";
852 $output .= "<head>\n";
853 $output .= "<title>Compass Rose</title>\n";
854 $output .= "<style type='text/css'>IMG.inrose {position:absolute; z-index:0;} IMG.inrose:hover {z-index:1;}</style>";
855 $output .= "</head>\n<body>\n";
856 //header sending example from img_assist img_assist_loader() around line 400
857
858 if (!$nid) {
859 $output .= t("A bizarre error has occurred, whereby no monument has " .
860 "been specified for display. I could show you a random one," .
861 " but I won't. Freaky, huh?");
862 return;
863 }
864 $node = node_load($nid);
865 if(_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) { echo("ADV:"); print_r($node);}
866 //get the monument ID from the nid
867 $monument = db_fetch_object(db_query('SELECT monumentID as mid ' .
868 'FROM {monument} WHERE nid = %d',
869 $node->nid));
870 // join files ON monument_image to get the filename
871 // and paths for all associated images.
872 $result = db_query(
873 "SELECT {monument_image}.nid, mid, taken_date, img_loc,
874 face_bearing, filepath FROM {monument_image} INNER JOIN {files} ON
875 {monument_image}.{nid}={files}.{nid} WHERE {monument_image}.mid = %d AND
876 {files}.{filename} LIKE 'thumbnail'", $monument->mid
877 );
878
879 $count = 0; //we'll count them again just for the hell of it.
880 $images = array();
881 while ($row = db_fetch_array($result)) {
882 // as in medium_view, we need to organise images
883 // by location they are taken from
884 $iloc = $row['img_loc'];
885 if(_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) { print_r($iloc);echo"-";print_r($row['nid']);echo" <br />";}
886 //array of img_loc with array of images belonging to each img_loc
887 $images[$iloc][$row['nid']] = $row;
888 $count++;
889 }
890 if(_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) {print_r($images);echo"<br />end image dump<br />";}
891 //first call the basic_view theme.
892 //$output .= theme_monument_basic_view($node);
893 // next set up a separate div and title for styling by themes if needed
894 $output .= "<div class='monument_view_advanced'>\n";
895 $output .= "<p class='title2'>".check_plain($node->title);
896 $output .= " <a href='javascript:window.close();'>Close</a> | <a href='javascript:window.print();'>Print</a></p>\n";
897
898 //now start outputting the ring-arrangements ONLY (no key_image or ad_hoc etc)
899 //the magic numbers in the image src are the ring radii in order of inner,
900 //medium and outer resp. (pixels)
901 $radii = array (1=> 133, 266, 399);
902 $imlink = "<img style='position: absolute; left:50px; top:50px;' src='" .
903 $base_url . "/node/monument/" .
904 "make_compass_rose/@r1/@r2/@r3' />\n";
905 $output .= t($imlink, array ( '@r1' => $radii[1],
906 '@r2' => $radii[2],
907 '@r3' => $radii[3]));
908 $imglocs = _monument_image_img_locations();
909
910 $thumbsize = 100; //thumbsize needs modification to take account of rotated size
911 for ($iloc=3;$iloc>=1;$iloc--) { //we only want ring1, ring2 and ring3
912 $imgloc = $imglocs[$iloc];
913 // $output .= t( "<h3>From %imgloc location </h3>\n",
914 // array ('%imgloc' => $imgloc));
915 // enter a loop through each image found in current location
916 if (_ASCENT_SOFTWARE_LIMITED_DEBUG_BROCH_MODULE) {print_r($images[$iloc]);echo"end images $iloc<br />";}
917 //only process if any images with current img_loc
918 if (sizeof($images[$iloc])) {
919 foreach ($images[$iloc] as $image) {
920 $fp = $image['filepath']; //filepath
921 //throw away file path info - just filename!
922 $fn = substr($fp, 1+strrpos($fp,'/'));
923 $bearing = $image['face_bearing']; //face bearing
924 $nid = $image['nid'];
925 //now generate a style tag for the image. 3rd and 4th args
926 //are ring co-ordinate centres, 5th and 6th
927 //args are the maximum image width and height
928 $style = _yield_image_position_style( 'future_imp', $bearing,
929 450, 450, $radii[$iloc], $thumbsize, $thumbsize);
930 // $title = "file: $fn, bearing: $bearing";
931 // $imlink = "<img class='inrose' title='@title' style='@style' " .
932 // "src='node/monument" .
933 // "/rotate/@fn/@thumbsize/@bearing' />\n";
934 // $output .= t($imlink, array ( '@fn' => $fn,
935 // '@thumbsize' => $thumbsize,
936 // '@title' => $title,
937 // '@style' => $style,
938 // '@bearing' => $bearing));
939 $fp2 = t("$base_url/node/monument/rotate/@fn/@thumbsize/@bearing", array ( '@fn' => $fn, '@thumbsize' => $thumbsize, '@bearing' => $bearing));
940 // $fp3 = file_create_url($fp2);//NOT going to work. url prepends file/ to path.
941 $img = t("<img class=\"inrose\" src=\"$fp2\" title=\"file:@fn, bearing: @bearing\" style=\"@style\" /> ",
942 array('@bearing' => $bearing, '@fn' => $fn, '@style' => $style));
943 $path = t("node/@nid", array('@nid' => $nid));
944 $output .= l($img, $path, NULL, NULL, NULL, FALSE, TRUE);
945 }
946 }
947 $thumbsize = $thumbsize*0.9; //reduce for each successive ring
948 }
949 $output .= "</body>\n</html>";
950 print $output;
951 //return $output; //returing the output will result in it being themed.
952 }
953
954 /**
955 * Output rotated image to browser. Done via callback in menu hook
956 * Image transparency would be nice (see _make_compass_rose)
957 *
958 * @param node to display
959 * @return HTML string with additional node information
960 */
961 function _monument_image_rotate($image=NULL, $width=100, $angle=0) {
962 //don't trust URL parameters to be safe
963 $image = trim(check_plain($image));
964 if (!is_numeric($angle)) { drupal_access_denied(); return; }
965 if (!is_numeric($width)) { drupal_access_denied(); return; }
966 //if we don't have GD2 functions, we can't generate the image
967 if (!function_exists('imagecreatetruecolor')) return;
968
969 //$image is target image name
970 //$width is target image width (height is calculated)
971 //$angle is required clockwise rotation in degrees
972
973 // Set headers
974 header('Expires: Mon, 01 Jan 1997 05:00:00 GMT');
975 header('Cache-Control: no-store, no-cache, must-revalidate');
976 header('Cache-Control: post-check=0, pre-check=0', false);
977 header('Pragma: no-cache');
978
979 header('Content-type: image/jpeg');
980 $image = "files/images/" . $image;//$src_im="dscn0102.jpg";
981 $src_im=imagecreatefromjpeg($image);//'files/images/dscn0155.jpg');
982 if(!isset($width)) $width=100; //default to 100 wide
983 if ($width<10) $width=10; //minimum width 10 pixels
984 if ($width>1024) $width=1024; //max width 1024 (memory!)
985 $size = getimagesize($image);
986 $sW = $size[0]; //source image width
987 if(!$sW) $sW=1; //prevent zero-divide error
988 $sH = $size[1]; //source image height
989 $factor = $width / $sW; //scale factor
990 $dW = $width; //destination image width
991 $dH = $sH * $factor;
992 $dH = (int) $dH; //destination image height
993 $dst_im = imagecreatetruecolor($dW,$dH) or die("Resize: Could not create image!"); imagecopyresampled($dst_im, $src_im, 0,0,0,0,$dW,$dH,$sW,$sH);
994 $text_color = imagecolorallocate ($dst_im, 130, 130, 130);
995 imagestring($dst_im, 0, 0, 0, "(c)07 RockStanza", $text_color);
996 imagestring($dst_im, 0, 0, 7, "Licence:GNU FDL", $text_color);
997 $output=imagerotate($dst_im,360-$angle,0xffffff);
998 imagejpeg($output);
999 imagedestroy($output);
1000 imagedestroy($src_im);
1001 imagedestroy($dst_im);
1002 }
1003
1004 /**
1005 * return a style entry for positioning the image im_id with given
1006 * facing bearing, ringcentre x,y coords $xc, $yc and a ring radius $img_width
1007 * is the pixel width of the image, used to adjust positioning to image centre,
1008 * $img_height similarly
1009 */
1010 function _yield_image_position_style( $im_id,$f_bearing,$xc,$yc,
1011 $radius,$img_width,$img_height)
1012 {
1013 if (!isset($f_bearing)) $f_bearing=0;
1014 if (!isset($xc)) $xc=300;
1015 if (!isset($yc)) $yc=300;
1016 if (!isset($radius)) $radius=120;
1017 if (!isset($img_width)) $img_width=100;
1018 if (!isset($img_height)) $img_height=100;
1019 $xi=$xc+$radius*sin($f_bearing*3.14/180.0)-$img_width/2;
1020 $yi=$yc-$radius*cos($f_bearing*3.14/180.0)-$img_height/2;
1021 $xi=(integer)$xi;
1022 $yi=(integer)$yi;
1023 //$im_id is not used yet, but is intended if separate stylesheet is being generated
1024 $style = "left:$xi"."px; top:$yi"."px;";
1025 return $style;
1026 }
1027
1028 /**
1029 * directly output a png representing a basic compass rose
1030 *
1031 */
1032 function _make_compass_rose($rad1=130, $rad2=260, $rad3=390) {
1033 header('Expires: Mon, 01 Jan 1997 05:00:00 GMT');
1034 header('Cache-Control: no-store, no-cache, must-revalidate');
1035 header('Cache-Control: post-check=0, pre-check=0', false);
1036 header('Pragma: no-cache');
1037 header("Content-type: image/png");
1038
1039 $extent=(integer)2*$rad3;
1040 $im = imagecreate($extent,$extent);
1041 $background_color = imagecolorallocate ($im, 249, 249, 249);
1042 $draw_color = imagecolorallocate ($im, 230, 150, 150);
1043 $text_color = imagecolorallocate ($im, 130, 130, 130);
1044 imagecolortransparent($im,$background_color);
1045 $centre=$rad3;
1046 imagearc($im,$centre,$centre,2*$rad1,2*$rad1,0,360,$draw_color);
1047 imagearc($im,$centre,$centre,2*$rad2,2*$rad2,0,360,$draw_color);
1048 imagearc($im,$centre,$centre,2*$rad3,2*$rad3,0,360,$draw_color);
1049 for($loop=0;$loop<360;){
1050 $angle=((float)$loop*3.141593)/180.0;
1051 $xs=$centre+0.5*$rad1*sin($angle);
1052 $ys=$centre-0.5*$rad1*cos($angle);
1053 $xe=$centre+$rad3*sin($angle);
1054 $ye=$centre-$rad3*cos($angle);
1055 imageline($im,$xs,$ys,$xe,$ye,$draw_color);
1056 $bearing = sprintf("%03s", strval($loop));
1057 imagestring($im, 3, $xs-10, $ys-7, $bearing, $text_color);
1058 $loop+=30;
1059 }
1060 imagestring($im, 3, $centre-20, $centre-$rad1, "Ring 1", $text_color);
1061 imagestring($im, 3, $centre-20, $centre-$rad2, "Ring 2", $text_color);
1062 imagestring($im, 3, $centre-20, $centre-$rad3, "Ring 3", $text_color);
1063
1064 imagepng($im);
1065 imagedestroy($im);
1066 }
1067
1068 /** Generate the list of first letters of monument posts by title
1069 * TODO Use cache mechanism.
1070 * @return $letter_links HTML links organised by post title
1071 */
1072 function _get_monument_letter_links_by_title() {
1073 $result = db_query(db_rewrite_sql(
1074 "SELECT title FROM {node} " .
1075 "WHERE {node}.{type} = 'monument'" .
1076 "ORDER BY {title} ASC"
1077 ));
1078 $index = array();
1079 $letter_links = '';
1080 do {
1081 $t = db_fetch_array($result);
1082 $letter=$t['title'][0];
1083 $index[$letter]=$letter; //store unique first letters in list
1084 } while ($t);
1085 foreach ($index as $fl) {
1086 $letter_links .= l($fl,"listbytitle/$fl",
1087 array('title' => "Monuments beginning $fl", 'target' => '_self'),
1088 NULL, NULL, FALSE, FALSE) . ' ';
1089 }
1090 return $letter_links;
1091 }