1: <?php
2:
3: namespace Alpha\Controller;
4:
5: use Alpha\Util\Logging\Logger;
6: use Alpha\Util\Logging\KPI;
7: use Alpha\Util\Config\ConfigProvider;
8: use Alpha\Util\Security\SecurityUtils;
9: use Alpha\Util\Extension\TCPDFFacade;
10: use Alpha\Util\Http\Request;
11: use Alpha\Util\Http\Response;
12: use Alpha\Util\Http\Session\SessionProviderFactory;
13: use Alpha\Util\File\FileUtils;
14: use Alpha\Model\Article;
15: use Alpha\Model\ArticleComment;
16: use Alpha\View\View;
17: use Alpha\View\ViewState;
18: use Alpha\View\Widget\Button;
19: use Alpha\Exception\SecurityException;
20: use Alpha\Exception\AlphaException;
21: use Alpha\Exception\RecordNotFoundException;
22: use Alpha\Exception\IllegalArguementException;
23: use Alpha\Exception\ResourceNotFoundException;
24: use Alpha\Exception\FileNotFoundException;
25: use Alpha\Model\ActiveRecord;
26: use Alpha\Controller\Front\FrontController;
27:
28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69:
70: class ArticleController extends ActiveRecordController implements ControllerInterface
71: {
72: 73: 74: 75: 76: 77: 78:
79: private static $logger = null;
80:
81: 82: 83: 84: 85:
86: public function __construct()
87: {
88: self::$logger = new Logger('ArticleController');
89: self::$logger->debug('>>__construct()');
90:
91:
92: parent::__construct('Public');
93:
94: self::$logger->debug('<<__construct');
95: }
96:
97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107:
108: public function doGET($request)
109: {
110: self::$logger->debug('>>doGET($request=['.var_export($request, true).'])');
111:
112: $config = ConfigProvider::getInstance();
113:
114: $params = $request->getParams();
115:
116: $body = '';
117:
118:
119: if (isset($params['title']) && (isset($params['pdf']) || $request->getHeader('Accept') == 'application/pdf')) {
120: try {
121: $title = str_replace($config->get('cms.url.title.separator'), ' ', $params['title']);
122:
123: if (isset($params['ActiveRecordType']) && class_exists($params['ActiveRecordType'])) {
124: $record = new $params['ActiveRecordType'];
125: } else {
126: $record = new Article();
127: }
128: $record->loadByAttribute('title', $title);
129: $this->record = $record;
130:
131: ActiveRecord::disconnect();
132:
133: $pdf = new TCPDFFacade($record);
134: $pdfData = $pdf->getPDFData();
135: $pdfDownloadName = str_replace(' ', '-', $record->get('title').'.pdf');
136:
137: $headers = array(
138: 'Pragma' => 'public',
139: 'Expires' => 0,
140: 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
141: 'Content-Transfer-Encoding' => 'binary',
142: 'Content-Type' => 'application/pdf',
143: 'Content-Length' => strlen($pdfData),
144: 'Content-Disposition' => 'attachment; filename="'.$pdfDownloadName.'";',
145: );
146:
147: return new Response(200, $pdfData, $headers);
148: } catch (IllegalArguementException $e) {
149: self::$logger->error($e->getMessage());
150: throw new ResourceNotFoundException($e->getMessage());
151: } catch (RecordNotFoundException $e) {
152: self::$logger->error($e->getMessage());
153: throw new ResourceNotFoundException($e->getMessage());
154: }
155: }
156:
157:
158: if ((isset($params['view']) && $params['view'] == 'edit') && (isset($params['title']) || isset($params['ActiveRecordOID']))) {
159: if (isset($params['ActiveRecordType']) && class_exists($params['ActiveRecordType'])) {
160: $record = new $params['ActiveRecordType'];
161: } else {
162: $record = new Article();
163: }
164:
165: try {
166: if (isset($params['title'])) {
167: $title = str_replace($config->get('cms.url.title.separator'), ' ', $params['title']);
168: $record->loadByAttribute('title', $title);
169: } else {
170: $record->load($params['ActiveRecordOID']);
171: }
172: } catch (RecordNotFoundException $e) {
173: self::$logger->warn($e->getMessage());
174: $body .= View::renderErrorPage(404, 'Failed to find the requested article!');
175:
176: return new Response(404, $body, array('Content-Type' => 'text/html'));
177: }
178:
179: ActiveRecord::disconnect();
180:
181: $this->record = $record;
182: $view = View::getInstance($record);
183:
184:
185: $this->setTitle($record->get('title').' (editing)');
186: $this->setDescription('Page to edit '.$record->get('title').'.');
187: $this->setKeywords('edit,article');
188:
189: $body .= View::displayPageHead($this);
190:
191: $message = $this->getStatusMessage();
192: if (!empty($message)) {
193: $body .= $message;
194: }
195:
196: $body .= $view->editView(array('URI' => $request->getURI()));
197: $body .= View::renderDeleteForm($request->getURI());
198:
199: $body .= View::displayPageFoot($this);
200: self::$logger->debug('<<doGET');
201:
202: return new Response(200, $body, array('Content-Type' => 'text/html'));
203: }
204:
205:
206: if (isset($params['title']) || isset($params['ActiveRecordOID'])) {
207: $KDP = new KPI('viewarticle');
208: if (isset($params['ActiveRecordType']) && class_exists($params['ActiveRecordType'])) {
209: $record = new $params['ActiveRecordType'];
210: } else {
211: $record = new Article();
212: }
213:
214: try {
215: if (isset($params['title'])) {
216: $title = str_replace($config->get('cms.url.title.separator'), ' ', $params['title']);
217:
218: $record->loadByAttribute('title', $title, false, array('OID', 'version_num', 'created_ts', 'updated_ts', 'title', 'author', 'published', 'content', 'headerContent'));
219: } else {
220: $record->load($params['ActiveRecordOID']);
221: }
222:
223: if (!$record->get('published')) {
224: throw new RecordNotFoundException('Attempted to load an article which is not published yet');
225: }
226:
227: $record->set('tags', $record->getOID());
228: } catch (IllegalArguementException $e) {
229: self::$logger->warn($e->getMessage());
230: throw new ResourceNotFoundException('The file that you have requested cannot be found!');
231: } catch (RecordNotFoundException $e) {
232: self::$logger->warn($e->getMessage());
233: throw new ResourceNotFoundException('The article that you have requested cannot be found!');
234: }
235:
236: $this->record = $record;
237: $this->setTitle($record->get('title'));
238: $this->setDescription($record->get('description'));
239:
240: $BOView = View::getInstance($record);
241:
242: $body .= View::displayPageHead($this);
243:
244: $message = $this->getStatusMessage();
245: if (!empty($message)) {
246: $body .= $message;
247: }
248:
249: $body .= $BOView->markdownView();
250:
251: $body .= View::displayPageFoot($this);
252:
253: $KDP->log();
254:
255: return new Response(200, $body, array('Content-Type' => 'text/html'));
256: }
257:
258:
259: if (isset($params['file'])) {
260: try {
261: $record = new Article();
262:
263:
264: if (mb_substr($params['file'], 0, 1) == '/') {
265: $record->loadContentFromFile($params['file']);
266: } else {
267: $record->loadContentFromFile($config->get('app.root').'docs/'.$params['file']);
268: }
269: } catch (IllegalArguementException $e) {
270: self::$logger->error($e->getMessage());
271: throw new ResourceNotFoundException($e->getMessage());
272: } catch (FileNotFoundException $e) {
273: self::$logger->warn($e->getMessage().' File path is ['.$params['file'].']');
274: throw new ResourceNotFoundException('Failed to load the requested article from the file system!');
275: }
276:
277: $this->record = $record;
278: $this->setTitle($record->get('title'));
279:
280: $BOView = View::getInstance($record);
281:
282: $body .= View::displayPageHead($this, false);
283:
284: $body .= $BOView->markdownView();
285:
286: $body .= View::displayPageFoot($this);
287:
288: return new Response(200, $body, array('Content-Type' => 'text/html'));
289: }
290:
291:
292: if (isset($params['start'])) {
293: return parent::doGET($request);
294: }
295:
296:
297: $record = new Article();
298: $view = View::getInstance($record);
299:
300:
301: $this->setTitle('Creating article');
302: $this->setDescription('Page to create a new article.');
303: $this->setKeywords('create,article');
304:
305: $body .= View::displayPageHead($this);
306:
307: $message = $this->getStatusMessage();
308: if (!empty($message)) {
309: $body .= $message;
310: }
311:
312: $fields = array('formAction' => $this->request->getURI());
313: $body .= $view->createView($fields);
314:
315: $body .= View::displayPageFoot($this);
316: self::$logger->debug('<<doGET');
317:
318: return new Response(200, $body, array('Content-Type' => 'text/html'));
319: }
320:
321: 322: 323: 324: 325: 326: 327: 328: 329:
330: public function doPUT($request)
331: {
332: self::$logger->debug('>>doPUT($request=['.var_export($request, true).'])');
333:
334: $config = ConfigProvider::getInstance();
335:
336: $params = $request->getParams();
337:
338: try {
339:
340: if (!$this->checkSecurityFields()) {
341: throw new SecurityException('This page cannot accept post data from remote servers!');
342: self::$logger->debug('<<doPUT');
343: }
344:
345: if (isset($params['markdownTextBoxRows']) && $params['markdownTextBoxRows'] != '') {
346: $viewState = ViewState::getInstance();
347: $viewState->set('markdownTextBoxRows', $params['markdownTextBoxRows']);
348: }
349:
350: if (isset($params['title']) || isset($params['ActiveRecordOID'])) {
351: if (isset($params['ActiveRecordType']) && class_exists($params['ActiveRecordType'])) {
352: $record = new $params['ActiveRecordType'];
353: } else {
354: $record = new Article();
355: }
356:
357: if (isset($params['title'])) {
358: $title = str_replace($config->get('cms.url.title.separator'), ' ', $params['title']);
359:
360: $record->loadByAttribute('title', $title, false, array('OID', 'version_num', 'created_ts', 'updated_ts', 'title', 'author', 'published', 'content', 'headerContent'));
361: } else {
362: $record->load($params['ActiveRecordOID']);
363: }
364:
365:
366: if (isset($params['uploadBut'])) {
367: $source = $request->getFile('userfile')['tmp_name'];
368: $dest = $record->getAttachmentsLocation().'/'.$request->getFile('userfile')['name'];
369:
370:
371: FileUtils::copy($source, $dest);
372:
373: if (!file_exists($dest)) {
374: throw new AlphaException('Could not move the uploaded file ['.$request->getFile('userfile')['name'].']');
375: }
376:
377:
378: $success = chmod($dest, 0666);
379:
380: if (!$success) {
381: throw new AlphaException('Unable to set read/write permissions on the uploaded file ['.$dest.'].');
382: }
383:
384: if ($success) {
385: self::$logger->action('File '.$source.' uploaded to '.$dest);
386: $this->setStatusMessage(View::displayUpdateMessage('File '.$source.' uploaded to '.$dest));
387: }
388: } elseif (isset($params['deletefile']) && $params['deletefile'] != '') {
389: $success = unlink($record->getAttachmentsLocation().'/'.$params['deletefile']);
390:
391: if (!$success) {
392: throw new AlphaException('Could not delete the file ['.$params['deletefile'].']');
393: }
394:
395: if ($success) {
396: self::$logger->action('File '.$record->getAttachmentsLocation().'/'.$params['deletefile'].' deleted');
397: $this->setStatusMessage(View::displayUpdateMessage('File '.$record->getAttachmentsLocation().'/'.$params['deletefile'].' deleted'));
398: }
399: } else {
400: self::$logger->debug('<<doPUT');
401:
402: return parent::doPUT($request);
403: }
404: } else {
405: throw new IllegalArguementException('No valid article ID provided!');
406: }
407: } catch (SecurityException $e) {
408: $this->setStatusMessage(View::displayErrorMessage($e->getMessage()));
409: self::$logger->warn($e->getMessage());
410: } catch (IllegalArguementException $e) {
411: $this->setStatusMessage(View::displayErrorMessage($e->getMessage()));
412: self::$logger->error($e->getMessage());
413: } catch (RecordNotFoundException $e) {
414: self::$logger->warn($e->getMessage());
415: $this->setStatusMessage(View::displayErrorMessage('Failed to load the requested article from the database!'));
416: } catch (AlphaException $e) {
417: $this->setStatusMessage(View::displayErrorMessage($e->getMessage()));
418: self::$logger->error($e->getMessage());
419: }
420:
421: $response = new Response(301);
422:
423: if ($this->getNextJob() != '') {
424: $response->redirect($this->getNextJob());
425: } else {
426: if ($this->request->isSecureURI()) {
427: $response->redirect(FrontController::generateSecureURL('act=Alpha\\Controller\\ActiveRecordController&ActiveRecordType=Alpha\Model\Article&ActiveRecordOID='.$record->getOID().'&view=edit'));
428: } else {
429: $title = str_replace(' ', $config->get('cms.url.title.separator'), $record->get('title'));
430: $response->redirect($config->get('app.url').'/a/'.$title.'/edit');
431: }
432: }
433:
434: self::$logger->debug('<<doPUT');
435:
436: return $response;
437: }
438:
439: 440: 441: 442: 443: 444: 445: 446: 447:
448: public function doDELETE($request)
449: {
450: self::$logger->debug('>>doDELETE($request=['.var_export($request, true).'])');
451:
452: $this->setUnitOfWork(array());
453:
454: self::$logger->debug('<<doDELETE');
455:
456: return parent::doDELETE($request);
457: }
458:
459: 460: 461: 462: 463: 464: 465:
466: public function during_displayPageHead_callback()
467: {
468: $config = ConfigProvider::getInstance();
469:
470: $params = $this->request->getParams();
471:
472: $html = '';
473:
474: if ($config->get('cms.highlight.provider.name') == 'Alpha\Util\Code\Highlight\HighlightProviderLuminous') {
475: $html .= '<link rel="StyleSheet" type="text/css" href="'.$config->get('app.url').'/css/luminous.css">';
476: $html .= '<link rel="StyleSheet" type="text/css" href="'.$config->get('app.url').'/css/luminous_light.css">';
477: }
478:
479: if ((isset($params['view']) && ($params['view'] == 'edit' || $params['view'] == 'create')) || (isset($params['ActiveRecordType']) && !isset($params['ActiveRecordOID']))) {
480:
481: $fieldid = ($config->get('security.encrypt.http.fieldnames') ? 'text_field_'.base64_encode(SecurityUtils::encrypt('content')).'_0' : 'text_field_content_0');
482:
483: $html .= '
484: <script type="text/javascript">
485: $(document).ready(function() {
486: $(\'[id="'.$fieldid.'"]\').pagedownBootstrap({
487: \'sanatize\': false
488: });
489: });
490: </script>';
491:
492: } elseif (isset($params['view']) && $params['view'] == 'print') {
493: $html .= '<link rel="StyleSheet" type="text/css" href="'.$config->get('app.url').'/css/print.css">';
494: }
495:
496: return $html;
497: }
498:
499: 500: 501: 502: 503: 504: 505:
506: public function insert_CMSDisplayStandardHeader_callback()
507: {
508: if ($this->request->getParam('token') != null) {
509: return '';
510: }
511:
512: if (!$this->record instanceof Article) {
513: return '';
514: }
515:
516: $config = ConfigProvider::getInstance();
517:
518: $html = '';
519:
520: if ($config->get('cms.display.standard.header')) {
521: $html .= '<p><a href="'.$config->get('app.url').'">'.$config->get('app.title').'</a> ';
522: $html .= 'Date Added: <em>'.$this->record->getCreateTS()->getDate().'</em> ';
523: $html .= 'Last Updated: <em>'.$this->record->getUpdateTS()->getDate().'</em> ';
524: $html .= 'Revision: <em>'.$this->record->getVersion().'</em></p>';
525: }
526:
527: $html .= $config->get('cms.header');
528:
529: return $html;
530: }
531:
532: 533: 534: 535: 536: 537: 538: 539:
540: public function ()
541: {
542: $config = ConfigProvider::getInstance();
543: $sessionProvider = $config->get('session.provider.name');
544: $session = SessionProviderFactory::getInstance($sessionProvider);
545:
546: $html = '';
547: $params = $this->request->getParams();
548:
549:
550: if (isset($this->record) && !$this->record->isTransient()) {
551: $this->setName($config->get('app.url').$this->request->getURI());
552: $this->setUnitOfWork(array($config->get('app.url').$this->request->getURI(), $config->get('app.url').$this->request->getURI()));
553: } else {
554: $this->setUnitOfWork(array());
555: }
556:
557: if ($this->record != null) {
558: if (isset($params['view']) && $params['view'] == 'detailed') {
559: if ($config->get('cms.display.comments')) {
560: $html .= $this->renderComments();
561: }
562:
563: if ($config->get('cms.display.tags')) {
564: $tags = $this->record->getPropObject('tags')->getRelatedObjects();
565:
566: if (count($tags) > 0) {
567: $html .= '<p>Tags:';
568:
569: foreach ($tags as $tag) {
570: $html .= ' <a href="'.$config->get('app.url').'/search/'.$tag->get('content').'">'.$tag->get('content').'</a>';
571: }
572: $html .= '</p>';
573: }
574: }
575:
576: if ($config->get('cms.display.votes')) {
577: $rating = $this->record->getArticleScore();
578: $votes = $this->record->getArticleVotes();
579: $html .= '<p>Average Article User Rating: <strong>'.$rating.'</strong> out of 10 (based on <strong>'.count($votes).'</strong> votes)</p>';
580: }
581:
582: if (!$this->record->checkUserVoted() && $config->get('cms.voting.allowed')) {
583: $URL = FrontController::generateSecureURL('act=Alpha\Controller\ActiveRecordController&ActiveRecordType=Alpha\Model\ArticleVote');
584: $html .= '<form action="'.$URL.'" method="post" accept-charset="UTF-8">';
585: $fieldname = ($config->get('security.encrypt.http.fieldnames') ? base64_encode(SecurityUtils::encrypt('score')) : 'score');
586: $html .= '<p>Please rate this article from 1-10 (10 being the best):'.
587: '<select name="'.$fieldname.'">'.
588: '<option value="1">1'.
589: '<option value="2">2'.
590: '<option value="3">3'.
591: '<option value="4">4'.
592: '<option value="5">5'.
593: '<option value="6">6'.
594: '<option value="7">7'.
595: '<option value="8">8'.
596: '<option value="9">9'.
597: '<option value="10">10'.
598: '</select></p> ';
599:
600: $fieldname = ($config->get('security.encrypt.http.fieldnames') ? base64_encode(SecurityUtils::encrypt('articleOID')) : 'articleOID');
601: $html .= '<input type="hidden" name="'.$fieldname.'" value="'.$this->record->getOID().'"/>';
602:
603: $fieldname = ($config->get('security.encrypt.http.fieldnames') ? base64_encode(SecurityUtils::encrypt('personOID')) : 'personOID');
604: $html .= '<input type="hidden" name="'.$fieldname.'" value="'.$session->get('currentUser')->getID().'"/>';
605:
606: $fieldname = ($config->get('security.encrypt.http.fieldnames') ? base64_encode(SecurityUtils::encrypt('statusMessage')) : 'statusMessage');
607: $html .= '<input type="hidden" name="'.$fieldname.'" value="Thank you for rating this article!"/>';
608:
609: $temp = new Button('submit', 'Vote!', 'voteBut');
610: $html .= $temp->render();
611:
612: $html .= View::renderSecurityFields();
613: $html .= '<form>';
614: }
615:
616: ActiveRecord::disconnect();
617:
618: if ($config->get('cms.allow.print.versions')) {
619: $html .= ' ';
620: $temp = new Button("window.open('".$this->record->get('printURL')."')", 'Open Printer Version', 'printBut');
621: $html .= $temp->render();
622: }
623:
624: $html .= ' ';
625: if ($config->get('cms.allow.pdf.versions')) {
626: $html .= ' ';
627: $temp = new Button("document.location = '".FrontController::generateSecureURL("act=Alpha\Controller\ArticleController&mode=pdf&title=".$this->record->get('title'))."';", 'Open PDF Version', 'pdfBut');
628: $html .= $temp->render();
629: }
630:
631:
632: if ($session->get('currentUser') instanceof Alpha\Model\Person && $session->get('currentUser')->inGroup('Admin')) {
633: $html .= ' ';
634: $button = new Button("document.location = '".FrontController::generateSecureURL('act=Alpha\Controller\ArticleController&mode=edit&ActiveRecordOID='.$this->record->getID())."'", 'Edit', 'editBut');
635: $html .= $button->render();
636: }
637: }
638:
639: if ($config->get('cms.display.standard.footer')) {
640: $html .= '<p>Article URL: <a href="'.$this->record->get('URL').'">'.$this->record->get('URL').'</a><br>';
641: $html .= 'Title: '.$this->record->get('title').'<br>';
642: $html .= 'Author: '.$this->record->get('author').'</p>';
643: }
644: }
645:
646: $html .= $config->get('cms.footer');
647:
648: return $html;
649: }
650:
651: 652: 653: 654: 655: 656: 657:
658: private function ()
659: {
660: $config = ConfigProvider::getInstance();
661: $sessionProvider = $config->get('session.provider.name');
662: $session = SessionProviderFactory::getInstance($sessionProvider);
663:
664: $html = '';
665:
666: $comments = $this->record->getArticleComments();
667: $commentsCount = count($comments);
668:
669: $URL = FrontController::generateSecureURL('act=Alpha\Controller\ActiveRecordController&ActiveRecordType=Alpha\Model\ArticleComment');
670:
671: $fields = array('formAction' => $URL);
672:
673: if ($config->get('cms.display.comments') && $commentsCount > 0) {
674: $html .= '<h2>There are ['.$commentsCount.'] user comments for this article</h2>';
675:
676: for ($i = 0; $i < $commentsCount; ++$i) {
677: $view = View::getInstance($comments[$i]);
678: $html .= $view->markdownView($fields);
679: }
680: }
681:
682: if ($session->get('currentUser') != null && $config->get('cms.comments.allowed')) {
683: $comment = new ArticleComment();
684: $comment->set('articleOID', $this->record->getID());
685:
686: $view = View::getInstance($comment);
687: $html .= $view->createView($fields);
688: }
689:
690: return $html;
691: }
692: }
693: