source: trunk/jpdd/class.jpdd.workshop.php @ 1416

Last change on this file since 1416 was 1416, checked in by Daniel Kahn Gillmor, 5 years ago

and use the proposal correctly

File size: 16.9 KB
Line 
1<?php  /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 2; -*-
2
3       */
4
5require_once('class.dkg.row.php');
6
7if (!class_exists('JPDD_Workshop')) {
8
9  class JPDD_Workshop extends DKG_Row {
10    function JPDD_Workshop($args = array()) {
11      $this->DKG_Row($args);
12    }
13   
14        var $_short_title_length = 35;
15
16        function getCreatePrivilege() {
17          return array('Update Workshops');
18        }
19        function getEditPrivilege() {
20          return array('Update Workshops');
21        }
22
23        function addAttendees($ids, $flavor) {
24          global $jpdd;
25          reset($ids);
26          while(list(,$id) = each($ids)) {
27        $jpdd->executeSQL('INSERT INTO attendance (person_id, workshop_id, flavor) VALUES ('.(int)$id.', '.$this->getID().', '.$jpdd->escStr($flavor).')');
28          }
29        }
30        function dropAttendees($ids, $flavor) {
31          global $jpdd;
32      if (count($ids) > 0) 
33        $jpdd->executeSQL('DELETE FROM attendance WHERE workshop_id = '.$this->getID().' AND flavor = '.$jpdd->escStr($flavor).' AND person_id IN ('.join(',', $ids).')');
34        }
35
36    function getAssignedRoomTitle() {
37      $rm = $this->getAssignedRoom();
38      return (is_null($rm) ? null : $rm->getTitle());
39    } 
40   
41    // returns NULL if nothing assigned.
42    function getAssignedRoom() {
43      global $jpdd;
44      if (!$jpdd->isEmpty($this->_room_id)) {
45        return $jpdd->getItem('room', (int)$this->_room_id);
46      } else {
47        return NULL;
48      }
49    }
50
51    function getShortTitle() {
52      if (is_null($this->_title)) 
53        return NULL;
54      if (strlen($this->_title) > $this->_short_title_length) {
55        if (!is_null($this->_short_title)) {
56          $ret = $this->_short_title;
57        } else {
58          $ret = array_shift(str_split($this->_title, $this->_short_title_length));
59        }
60      } else
61        $ret = $this->_title;
62
63      return htmlentities($ret);
64    }
65   
66        function getFormInnards() {
67          global $jpdd;
68      $jpdd->prepClass('room');
69      $room = $this->getAssignedRoom();
70      $openrooms = JPDD_Room::getOpenRooms();
71     
72      $jpdd->addTinyMCE();
73      $jpdd->addSourcedScript('scripts/dkg.base.js');
74      $jpdd->addScriptChunk('DKG.onLoadScripts.push(\'DKG.hideOnLength("'.$this->getTitle(true).'", '.(int)$this->_short_title_length.', "short_title_label");\');');
75          return '<div>
76<label>Title<br/><input type="text" size="72" name="title" onchange="DKG.hideOnLength(this.value, '.(int)$this->_short_title_length.',\'short_title_label\');" value="'.$this->getTitle(true).'"/></label><br/>
77<label id="short_title_label">Short Title <span class="notes">(needed for printing when title is too long)</span><br/><input type="text" maxlength="'.(int)$this->_short_title_length.'" name="short_title" value="'.htmlentities($this->_short_title).'"/></label><br/>
78<label>Description<br/><textarea name="description" rows="20" cols="70">'.$this->getDescription(true, true).'</textarea></label>
79<label>Private Notes<br/><textarea name="private_notes" rows="3" cols="70">'.$jpdd->FilterBlock($this->_private_notes, true).'</textarea></label>
80<fieldset><legend>Attendance</legend>
81<label><input type="text" size="2" maxlength="2" name="min_attendees" value="'.($jpdd->isEmpty($this->_min_attendees) ? '' : (int)$this->_min_attendees).'"/> Minimum</label><br/>
82<label><input type="text" size="2" maxlength="2" name="max_attendees" value="'.($jpdd->isEmpty($this->_max_attendees) ? '' : (int)$this->_max_attendees).'"/> Maximum</label>
83</fieldset>
84'.$this->getM2MEditView('person', 'presenter', ucfirst($jpdd->getPresenterNamePlural()), 'Add '.ucfirst($jpdd->getPresenterName()), 'last_name IS NOT NULL', NULL, 7).'
85'.$this->getM2MEditView('category', 'workshop_category', 'Categories', 'Add Category').'
86<fieldset><legend>Room Assignment</legend><select name="room_id">
87'.(is_null($room) ? '' : '<option value="'.$room->getID().'" selected>'.htmlentities($room->getTitle()).'</option>').'
88<option value="" '.(is_null($room) ? 'selected ' : '').'>none</option>
89'.join("\n", array_map(create_function('$x', 'return "<option value=\"".(int)$x->getID()."\">".htmlentities($x->getTitle())."</option>";'), $openrooms)).'
90</select>
91</fieldset>
92';
93        }
94
95    function getNext($category = NULL) {
96      global $jpdd;
97      $sql = 'SELECT * FROM workshop'.(is_null($category) ? '' : ' JOIN workshop_category ON (workshop.id = workshop_category.workshop_id)').
98        ' WHERE title > '.$jpdd->escStr($this->_title).(is_null($category) ? '' : ' AND category_id = '.(int)$category->getID()).
99        ' AND event_id = '.$this->_event_id.
100        ' ORDER BY title ASC LIMIT 1';
101      $next = $jpdd->getSeriesFromSQL($sql, 'JPDD_Workshop');
102      return array_shift($next);
103    }
104    function getPrev($category = NULL) {
105      global $jpdd;
106      $sql = 'SELECT * FROM workshop'.(is_null($category) ? '' : ' JOIN workshop_category ON (workshop.id = workshop_category.workshop_id)').
107        ' WHERE title < '.$jpdd->escStr($this->_title).(is_null($category) ? '' : ' AND category_id = '.(int)$category->getID()).
108        ' AND event_id = '.$this->_event_id.
109        ' ORDER BY title DESC LIMIT 1';
110      $next = $jpdd->getSeriesFromSQL($sql, 'JPDD_Workshop');
111      return array_shift($next);
112    }
113
114        function applyPostForm() {
115          $this->_title = $_POST['title'];
116          $this->_short_title = (array_key_exists('short_title', $_POST) ? $_POST['short_title'] : NULL);
117          $this->_description = $_POST['description'];
118          $this->_private_notes = $_POST['private_notes'];
119          $this->_min_attendees = $_POST['min_attendees'];
120          $this->_max_attendees = $_POST['max_attendees'];
121      $this->_room_id = (array_key_exists('room_id', $_POST) ? $_POST['room_id'] : NULL);
122        }
123
124        function handleEdit() {
125          global $jpdd;
126          $this->applyPostForm();
127          $res = $jpdd->executeSQL('UPDATE '.$this->getSingletonTable().' SET '.
128                                                           'title = '.$jpdd->stringOrDefault($this->_title).', '.
129                                                           'short_title = '.$jpdd->stringOrDefault($this->_short_title).', '.
130                                                           'description = '.$jpdd->stringOrDefault($this->_description).', '.
131                                                           'private_notes = '.$jpdd->stringOrDefault($this->_private_notes).', '.
132                                                           'room_id = '.$jpdd->intOrDefault($this->_room_id).', '.
133                                                           'min_attendees = '.$jpdd->intOrDefault($this->_min_attendees).', '.
134                                                           'max_attendees = '.$jpdd->intOrDefault($this->_max_attendees).' WHERE '.
135                                                           'id = '.$this->getID(), false);
136          if (false === $res) {
137        $logid = $jpdd->log('edit workshop '.(int)$this->_id.' db update failed with pg error: '.pg_last_error($jpdd->_db));
138                return 'editing this workshop failed during insertion into the database for some reason (error: '.(int)$logid.')';
139          } else {
140                $this->JPDD_Workshop(array('id' => $this->getID()));
141                $this->addAttendees($jpdd->getPostedIDs('add_presenter'), 'presenter');
142                $this->dropAttendees($jpdd->getPostedIDs('remove_presenter'), 'presenter');
143        $this->addM2MPeers('category', 'workshop_category', $jpdd->getPostedIDs('add_workshop_category'));
144        $this->dropM2MPeers('category', 'workshop_category', $jpdd->getPostedIDs('remove_workshop_category'));
145                return NULL;
146          }
147        }
148
149        function handleCreation() {
150          // just insert a new row, grab the created ID, and re-initialize
151          // ourselves, returning true if successful.
152          global $jpdd;
153          $this->applyPostForm();
154          $res = $jpdd->executeSQL('INSERT INTO '.$this->getSingletonTable().' (title, short_title, description, private_notes, min_attendees, max_attendees, event_id, room_id, proposal_id) VALUES ('.
155                                                           $jpdd->stringOrDefault($this->_title).', '.
156                                                           $jpdd->stringOrDefault($this->_short_title).', '.
157                                                           $jpdd->stringOrDefault($this->_description).', '.
158                                                           $jpdd->stringOrDefault($this->_private_notes).', '.
159                                                           $jpdd->intOrDefault($this->_min_attendees).', '.
160                                                           $jpdd->intOrDefault($this->_max_attendees).', '.
161                               $jpdd->getActiveEventID().', '.
162                               $jpdd->intOrDefault($this->_room_id).', '.
163                               $jpdd->intOrDefault($this->_proposal_id).
164                               ')', false);
165          if (false === $res) {
166        $logid = $jpdd->log('create workshop failed with pg error: '.pg_last_error($jpdd->_db));
167                return 'database insertion failed for some reason (error: '.(int)$logid.').';
168          } else {
169                $this->JPDD_Workshop(array('data' => $jpdd->getSingletonFromSQL('SELECT * from workshop WHERE id = currval(\'workshop_id_seq\')')));
170                $this->addAttendees($jpdd->getPostedIDs('add_presenter'), 'presenter');
171        $this->addM2MPeers('category', 'workshop_category', $jpdd->getPostedIDs('add_workshop_category'));
172                return NULL;
173          }
174        }
175
176    function getPresentersWithOrgs() {
177      return $this->getPersonListWithOrgs($this->getM2MPeers('person', 'presenter'));
178    }
179
180    function getPersonListWithOrgs($people) {
181      if (count($people)) 
182        return '<ul>'.join("\n", array_map(create_function('$x', 'return "<li>".$x->getNameWithOrgs()."</li>\n";'), $people))."\n</ul>\n";
183      return '';
184    }
185
186    function getAudienceListBySchoolCount() {
187      global $jpdd;
188      $sql = 'SELECT organization.id, MAX(organization.title) AS title, COUNT(*) AS count  FROM audience LEFT JOIN affiliation ON (audience.person_id = affiliation.person_id) LEFT JOIN organization ON (affiliation.organization_id = organization.id)  WHERE workshop_id = '.$this->getID().' GROUP BY organization.id ORDER BY MAX(organization.title)';
189      $x = $jpdd->getSeriesFromSQL($sql);
190      // FIXME: link these schools!
191      return "<ul>\n".join('', array_map(create_function('$x', 'global $jpdd; return "<li>".(int)$x["count"]." ".(is_null($x["title"]) ? "unaffiliated" : " from <a href=\"".$jpdd->Path("organization", (int)$x["id"])."\">".htmlentities($x["title"])."</a>");'), $x))."</ul>\n";
192    }
193
194    function getOverviewRowWithAttendence() {
195      return '<tr><td>'.$this->getLinkedTitle().$this->getPresentersWithOrgs().'</td>'.
196        '<td class="attendance">'.(int)$this->_max_attendees.'</td>'.
197        '<td class="attendance">'.(int)$this->_min_attendees.'</td>'.
198        '<td class="attendance">'.$this->getAudienceCount().'</td>'.
199        '<td>'.$this->getAudienceListBySchoolCount().'</td></tr>'."\n";
200    }
201
202    function getNextPrevLinks() {
203      $next = $this->getNext();
204      $prev = $this->getPrev();
205      return (is_null($prev) ? '' : '<a title="Previous Workshop" class="iterator" href="'.$prev->getViewPath().'">Previous Workshop</a> ').
206        (is_null($next) ? '' : '<a title="Next Workshop" class="iterator" href="'.$next->getViewPath().'">Next Workshop</a>');
207    }
208
209    function getPDFLinks() {
210      global $jpdd;
211      if ($jpdd->isAuthenticated()) {
212        if ($jpdd->_authenticated_user->hasAllOfThesePrivileges('Update Workshops')) {
213          $pdfs = array('Door Printout' => 'door',
214                        'Signup Sheet' => 'signup');
215          return '<div class="pdf-links">'."\n".join('', array_map(create_function('$t, $v', 'global $jpdd; return "<a href=\"".$jpdd->Path("printout", "workshop", '.$this->getID().', $v.".pdf")."\">".$t."</a>\n";'), array_keys($pdfs), $pdfs))."</div>\n";
216        }
217      } else {
218        return '';
219      }
220    }
221        function getDetailTopInnards() {
222      return $this->getPDFLinks().($this->isFull() ? '<div class="closed">This workshop is full and closed to new signups.</div>' : '').
223        $this->getPresentersWithOrgs();
224    }
225
226    function getAudienceCount() {
227      global $jpdd;
228      return $jpdd->getValueFromSQL('SELECT COUNT(*) AS foo FROM attendance WHERE workshop_id = '.(int)$this->getID().' AND flavor = '.$jpdd->escStr('audience'), 'foo');
229    }
230    function getTotalPeopleCount() {
231      global $jpdd;
232      return $jpdd->getValueFromSQL('SELECT COUNT(*) AS foo FROM attendance WHERE workshop_id = '.(int)$this->getID(), 'foo');
233    }
234    function isFull() {
235      return $this->getAudienceCount() >= $this->_max_attendees;
236    }
237
238    function getSignupForm() {
239      global $jpdd;
240      if ($jpdd->isSignupClosed())
241        return '';
242      return '<form action="'.$jpdd->Path('signup', 'workshop', $this->getID()).'" method="post">
243'.$jpdd->getFormTokenHiddenInput().'
244<input class="submit" type="submit" name="xx" value="Sign up for this workshop"/>
245</form>';
246    }
247
248    function getSignupCancellationForm() {
249      global $jpdd;
250      if ($jpdd->isSignupClosed())
251        return '';
252      return '<form action="'.$jpdd->Path('signup', 'workshop', $this->getID()).'" method="post">
253'.$jpdd->getFormTokenHiddenInput().'
254<input type="hidden" name="cancel" value="cancel"/>
255<input class="submit" type="submit" name="xx" value="Cancel my registration for this workshop"/>
256</form>';
257    }
258
259        function getDetailInnards() {
260          global $jpdd;
261      $editor = $jpdd->isAuthenticated() && $jpdd->_authenticated_user->canEdit($this);
262
263      $ret = '';
264
265      $room = $this->getAssignedRoom();
266      if (!is_null($room)) {
267        $ret .= '<p class="room-assignment">This workshop will take place in room '.htmlentities($room->getTitle()).'</p>';
268      }
269
270      if ($jpdd->isAuthenticated()) {
271        if (0 == $jpdd->_authenticated_user->getAttendanceCount() &&
272            !$this->isFull()) {
273          $ret .= $this->getSignupForm();
274        } elseif ($jpdd->_authenticated_user->isAttendingWorkshop($this->getID(), 'audience')) {
275          $ret .= $this->getSignupCancellationForm();
276        } 
277      }
278
279      if ((!is_null($this->_private_notes)) && $editor)
280         $ret .= '<div class="private-notes"><div class="title">Private Notes</div><div class="body">'.$jpdd->filterBlock($this->_private_notes).'</div></div>';
281
282      if ($jpdd->isAuthenticated()) {
283        $audience = $this->getM2MPeers('person', 'audience');
284        if (count($audience))
285          $ret .= '<div>Attending:'.$this->getPersonListWithOrgs($audience).'</div>';
286      }
287
288      if ($editor) {
289        $ret .= '<div class="stats">Min. Attendees: '.(int)($this->_min_attendees).'<br/>'.
290          'Max. Attendees: '.(int)($this->_max_attendees).'<br/>'.
291          'Current Attendees: '.count($this->getM2MPeers('person','audience'));
292        $proposal = $this->getProposal();
293        if (!is_null($proposal))
294          $ret .= 'From proposal: '.$proposal->getLinkedTitle().'<br/>';
295        $ret .= '</div>';
296      }
297      $cats = $this->getM2MPeers('category', 'workshop_category');
298      if (count($cats) > 0) {
299        $ret .=  'Categories: <ul>'."\n";
300        reset($cats);
301        while(list(,$cat) = each($cats)) {
302          $next = $this->getNext($cat);
303          $prev = $this->getPrev($cat);
304          $ret .= '<li>'.(is_null($prev) ? '' : '<a class="iterator" title="Previous Workshop in '.htmlentities($cat->getTitle()).'" href="'.$prev->getViewPath().'">&lt;</a> ').
305            $cat->getLinkedTitle().
306            (is_null($next) ? '' : ' <a class="iterator" title="Next Workshop in '.htmlentities($cat->getTitle()).'" href="'.$next->getViewPath().'">&gt;</a>'."\n");
307        }
308        $ret .= "</ul>\n";
309      }
310      $ret .= $this->getNextPrevLinks();
311          return $ret;
312        }
313
314    // make it all stay on the same page, if possible..
315    function writeToPDF(&$pdf) {
316      $font = 'helvetica';
317      $titlesize = 20;
318      $bodysize = 12;
319      $linesp = 1.2;
320      $title = html_entity_decode($this->getTitle());
321      $txt = $this->getDescription();
322      // convert back to plain text:
323      $txt = html_entity_decode(strip_tags(trim(preg_replace(array('|<br */?>|i', '|<p */?>|i'), array("\n", "\n\n"), $txt))), ENT_QUOTES);
324      $wid = $pdf->_page_width - 2*$pdf->_margin;
325
326      $pres = $this->getM2MPeers('person', 'presenter');
327      $room = $this->getAssignedRoom();
328      $prestxt = join("\n", array_merge(array('To be held in room '.$room->getTitle()), array_map(create_function('$p', 'return "  ".trim(strip_tags($p->getNameWithOrgs()));'), $pres)));
329
330      $hi = 0;
331      $pdf->SetFont($font, 'B', $titlesize);
332      $hi += ($pdf->LineCount($title, $wid) * $titlesize*$linesp);
333      $pdf->SetFont($font, 'B', $bodysize);
334      $hi += ($pdf->LineCount($prestxt, $wid) * $bodysize*$linesp);
335      $hi += $bodysize/2;
336
337      $pdf->SetFont($font, '', $bodysize);
338      $hi += ($pdf->LineCount($txt, $wid) * $bodysize*$linesp);
339
340      if (($pdf->GetY() + $hi + $this->_margin) > $pdf->_page_height) 
341        $pdf->newPage();
342
343      $pdf->SetFont($font, 'B', $titlesize);
344      $pdf->MultiCell($wid, $titlesize*$linesp, $title, 0, 'L');
345      $pdf->SetFont($font, 'B', $bodysize);
346      $pdf->MultiCell($wid, $bodysize*$linesp, $prestxt, 0, 'L');
347      $pdf->SetY($pdf->GetY() + $bodysize/2);
348      $pdf->SetFont($font, '', $bodysize);
349      $pdf->MultiCell($wid, $bodysize*$linesp, $txt, 0, 'L');
350     
351      // add some space at the bottom, if possible:
352      $pdf->SetY($pdf->GetY() + 20);
353    }
354    function getProposal() {
355      if (is_null($this->_proposal_id))
356        return null;
357      global $jpdd;
358      $map = $jpdd->prepClass('proposal');
359      return new $map['classname'](array('id' => (int)$this->_proposal_id));
360    }
361
362    function getPDF($pdfname) {
363      require_once('class.jpdd.pdf.php');
364      $ret = new JPDD_PDF();
365      if ($pdfname == 'door.pdf') {
366        $ret->addDoorPage($this);
367      } elseif ($pdfname == 'signup.pdf') {
368        $ret->addSignupSheet($this);
369      } else {
370        return NULL;
371      }
372      return $ret;
373    }
374
375  }
376}
Note: See TracBrowser for help on using the repository browser.