OPiQuotations  v.02.00.00 — October 26, 2015
 All Classes Namespaces Files Functions Variables Pages
OPiQuotation.inc
Go to the documentation of this file.
1 <?php /* -*- coding: utf-8 -*- */
2 
3 /** \file OPiQuotation.inc
4  * (October 25, 2015)
5  *
6  * \brief
7  * Class quotation (text, author…) or maxim (text, nation…).
8  *
9  * Piece of OPiQuotations.
10  * https://bitbucket.org/OPiMedia/opiquotations
11  *
12  * GPLv3 --- Copyright (C) 2014, 2015 Olivier Pirson
13  * http://www.opimedia.be/
14  *
15  * @package OPiQuotations
16  */
17 namespace OPiQuotations;
18 
19 
20 /**
21  * \brief
22  * Class quotation (text, author…) or maxim (text, nation…).
23  */
24 class OPiQuotation {
25  /**
26  * \brief
27  * Construct a quotation/maxim.
28  *
29  * See to_html().
30  *
31  * @param int $id Id of the quotation (must be > 0)
32  * @param string $text Text of the quotation (should not contain \htmlonly'@@@#'\endhtmlonly and \htmlonly'#@@@'\endhtmlonly)
33  * @param bool $is_maxim true if maxim (of a possibly nation), false if quotation (of an possibly author)
34  * @param bool $is_marked true if marked quotation/maxim, else false
35  * @param null|string $translation Possibly translation of the text (should not contain \htmlonly'@@@#'\endhtmlonly and \htmlonly'#@@@'\endhtmlonly)
36  * @param null|string $subject Possibly subject
37  * @param null|string $nation_author Possibly nation if maxim, else possibly author
38  * @param null|string $work Possibly work if quotation (must be null if $is_maxim)
39  */
40  public function __construct($id, $text,
41  $is_maxim=false,
42  $is_marked=false,
43  $translation=null,
44  $subject=null, $nation_author=null, $work=null) {
45  #DEBUG
46  assert('is_int($id)');
47  assert('$id > 0');
48  assert('is_string($text)');
49  assert('is_bool($is_maxim)');
50  assert('is_bool($is_marked)');
51  assert('($translation === null) || is_string($translation)');
52  assert('($subject === null) || is_string($subject)');
53  assert('($nation_author === null) || is_string($nation_author)');
54  assert('($work === null) || is_string($work)');
55  #DEBUG_END
56 
57  $this->id = $id;
58  $this->text = $text;
59  $this->maxim = $is_maxim;
60  $this->marked = $is_marked;
61  $this->translation = $translation;
62  $this->subject = $subject;
63  if ($is_maxim) {
64  #DEBUG
65  assert('$work === null');
66  #DEBUG_END
67 
68  $this->nation = $nation_author;
69  }
70  else {
71  $this->author = $nation_author;
72  $this->work = $work;
73  }
74  }
75 
76 
77 
78  /**
79  * \brief
80  * Return $s formatted in HTML to be displayed.
81  *
82  * - HTML tags &lt;b>, &lt;c>, &lt;i>, &lt;sub>, &lt;sup> are used (the other tags are displayed as it is).
83  * - The special character ¨ (diaeresis, U+00A8) is used to justify to the right.
84  * - Beginning (at the beginning of the line and after an end of line) double space is used to indentation.
85  *
86  * @param string $s (should not contain \htmlonly'@@@#'\endhtmlonly and \htmlonly'#@@@'\endhtmlonly)
87  *
88  * @return string
89  */
90  private static function _format_correct_html($s) {
91  #DEBUG
92  assert('is_string($s)');
93  # assert('preg_match(\'/ $/\', $s) === 0'); // check if no final space
94  #DEBUG_END
95 
96  $s = preg_replace('/<(b|c|i|sub|sup|\/b|\/c|\/i|\/sub|\/sup)>/', '@@@#$1#@@@', $s); // protect some HTML tags
97 
98  $s = htmlspecialchars($s); // escape special HTLM characters
99 
100  // Restore HTML tags previously protected
101  $s = preg_replace('/@@@#(b|i|sub|sup|\/b|\/i|\/sub|\/sup)#@@@/', '<$1>', $s);
102  $s = preg_replace('/@@@#c#@@@/', '<span class="block-center">', $s);
103  $s = preg_replace('/@@@#c#@@@/', '<span class="block-center">', $s);
104  $s = preg_replace('/@@@#\/c#@@@\n?/', '</span>', $s);
105 
106  // Special character ¨ to justify this line to the right
107  $s = preg_replace('/¨(.+)\n?/', '<span class="block-right">$1</span>', $s);
108 
109  // Non-breaking space with after « and before », ! and ?
110  $s = preg_replace('/« /', '«&nbsp;', $s);
111  $s = preg_replace('/ (»|!|\?)/', '&nbsp;$1', $s);
112 
113  // Non-breaking thin space before : and ;
114  $s = preg_replace('/ (:|;)/', '<span class="nowrap">&thinsp;$1</span>', $s);
115 
116  // Spaces
117  $s = preg_replace('/(^|\n) /', '$1&emsp;&emsp;', $s);
118  $s = preg_replace('/ /', '&nbsp;&nbsp;', $s);
119  $s = preg_replace('/(^|\n) /', '&nbsp;', $s);
120 
121  // HTML tag to newline
122  $s = preg_replace('/\n/', '<br>', $s);
123 
124  #DEBUG
125  # assert('preg_match(\'/¨/\', $s) === 0'); // check if no character ¨
126  # assert('preg_match(\'/\\\'/\', $s) === 0'); // check if no character ' (use ’ instead)
127  #DEBUG_END
128 
129  return $s;
130  }
131 
132 
133  /**
134  * \brief
135  * If $search !== null
136  * then each occurence of $search is surrounded by '&lt;span class="highlight">' and '&lt;/span>'.
137  *
138  * @param string $s
139  * @param null|string $search
140  *
141  * @return string
142  */
143  private static function _highlight_html($s, $search=null) {
144  #DEBUG
145  assert('is_string($s)');
146  assert('($search === null) || is_string($search)');
147  #DEBUG_END
148 
149  if ($search !== null && $search !== '') {
150  $search = preg_replace('/\n/', '<br>', $search); // split on HTML tags
151 
152  $pieces = preg_split('/(<.+?()>|&.+?;)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE); // (added () in regexp to avoid bug in PHPstripDEBUG)
153  foreach ($pieces as &$piece) {
154  if ((preg_match('/^<.+>$/', $piece) !== 1) && (preg_match('/^&.+;$/', $piece)) !== 1) { // it's NOT a HTML tag or entity
155  $piece = preg_replace('/('.preg_quote($search, '/').')/i', '<span class="highlight">$1</span>', $piece);
156  }
157  }
158 
159  return implode($pieces);
160  }
161  else {
162  return $s;
163  }
164  }
165 
166 
167 
168  /**
169  * \brief
170  * Return null or the author of the quotation.
171  *
172  * @return null|string
173  */
174  public function author() {
175  return ($this->maxim
176  ? null
177  : $this->author);
178  }
179 
180 
181  /**
182  * \brief
183  * Return the id of the quotation/maxim.
184  *
185  * @return id > 0
186  */
187  public function id() {
188  return $this->id;
189  }
190 
191 
192  /**
193  * \brief
194  * Return true if is a maxim (of a possibly nation),
195  * false if is a quotation (of a possibly author).
196  *
197  * @return bool
198  */
199  public function is_maxim() {
200  return $this->maxim;
201  }
202 
203 
204  /**
205  * \brief
206  * Return true if is a marked quotation/maxim,
207  * else false.
208  *
209  * @return bool
210  */
211  public function is_marked() {
212  return $this->marked;
213  }
214 
215 
216  /**
217  * \brief
218  * Return null or the nation of the maxim.
219  *
220  * @return null|string
221  */
222  public function nation() {
223  return ($this->maxim
224  ? $this->nation
225  : null);
226  }
227 
228 
229  /**
230  * \brief
231  * Return null or the subject of the quotation/maxim.
232  *
233  * @return null|string
234  */
235  public function subject() {
236  return $this->subject;
237  }
238 
239 
240  /**
241  * \brief
242  * Return the text of the quotation/maxim.
243  *
244  * @return string
245  */
246  public function text() {
247  return $this->text;
248  }
249 
250 
251  /**
252  * \brief
253  * Return the complete quotation/maxim in a HTML format.
254  *
255  * Layout
256  * - for a quotation:
257  *\code
258 id
259  subject (mark)
260 text | translation
261  author
262  work
263  *\endcode
264  *
265  * - for a maxim:
266  *\code
267 id
268  subject (mark)
269 text | translation
270  nation
271  *\endcode
272  *
273  * In text and translation:
274  * - HTML tags &lt;b>, &lt;c>, &lt;i>, &lt;sub>, &lt;sup> are used (the other tags are displayed as it is).
275  * - The special character ¨ (diaeresis, U+00A8) is used to justify to the right.
276  * - Beginning (at the beginning of the line and after an end of line) double space is used to indentation.
277  *
278  * If $search !== null
279  * then each occurence of $search founded is surrounded by '&lt;span class="highlight">' and '&lt;/span>'.
280  *
281  * If $add_link
282  * then add links to id, subject, nation, author and work.
283  *
284  * If $link_target
285  * then add a target attribut to each link.
286  *
287  * The subject is surrounded by $subject_tag.
288  *
289  * $uneOPiCitation_link is use to the external id link.
290  *
291  * @param null|string $search
292  * @param bool $add_link
293  * @param null|string $link_target
294  * @param string $subject_tag
295  * @param string $uneOPiCitation_link
296  *
297  * @return string
298  */
299  public function to_html($search=null, $add_link=true, $link_target=null,
300  $subject_tag='h2', $uneOPiCitation_link='uneOPiCitation.php') {
301  #DEBUG
302  assert('($search === null) || is_string($search)');
303  assert('is_bool($add_link)');
304  assert('($link_target === null) || is_string($link_target)');
305  assert('is_string($subject_tag)');
306  assert('is_string($uneOPiCitation_link)');
307  #DEBUG_END
308 
309  $link_target = ($link_target
310  ? ' target="'.$link_target.'"'
311  : '');
312 
313  // Maxim or quotation
314  $is_marked = ($this->is_marked()
315  ? ' marked'
316  : '');
317 
318  $a = array($this->is_maxim()
319  ? '<section id="maxim-'.$this->id().'" class="maxim'.$is_marked.'">'
320  : '<section id="quotation-'.$this->id().'" class="quotation'.$is_marked.'">');
321 
322  unset($is_marked);
323 
324  $a[] = ' <header>';
325 
326  // Marked ?
327  if ($this->is_marked()) {
328  $a[] = ' <div class="mark"></div>';
329  }
330 
331  // Id
332  $a[] = ' <div class="id">'.($add_link
333  ? ('<a href="./?id='.$this->id().'"'.$link_target.'>'.$this->id()
334  .'</a><a href="'.$uneOPiCitation_link.'?id='.$this->id()
335  .'" target="_blank">&#8599;&#10102;</a>')
336  : $this->id()).'</div>';
337 
338  if ($this->subject() !== null) { // subject
339  $html = $this->_highlight_html(htmlspecialchars($this->subject()), $search);
340  $a[] = ' <'.$subject_tag.' class="subject">'.($add_link
341  ? '<a href="./?subject='.rawurlencode($this->subject()).'"'.$link_target.'>'.$html.'</a>'
342  : $html).'</'.$subject_tag.'>';
343  }
344 
345  $a[] = ' </header>';
346 
347  if ($this->translation() === null) { // text
348  $a[] = ' <div class="text"><cite>'.$this->_highlight_html($this->_format_correct_html($this->text()), $search).'</cite></div>';
349  }
350  else { // text, translation
351  $a[] = ' <div class="text_translation">
352  <div class="text"><cite>'.$this->_highlight_html($this->_format_correct_html($this->text()), $search).'</cite></div>
353  <div class="translation"><cite>'.$this->_highlight_html($this->_format_correct_html($this->translation()), $search).'</cite></div>
354  <div class="clear"></div>
355  </div>';
356  }
357 
358  $a[] = ' <footer>';
359 
360  if ($this->is_maxim()) { // nation
361  #DEBUG
362  assert('$this->author() === null');
363  assert('$this->work() === null');
364  #DEBUG_END
365 
366  if ($this->nation() !== null) {
367  $html = $this->_highlight_html(htmlspecialchars($this->nation()), $search);
368  $a[] = ' <div class="nation">'.($add_link
369  ? '<a href="./?nation='.rawurlencode($this->nation()).'"'.$link_target.'>nation '.$html.'</a>'
370  : 'nation '.$html).'</div>';
371  }
372  }
373  else { // author, work
374  #DEBUG
375  assert('$this->nation() === null');
376  #DEBUG_END
377 
378  if ($this->author() !== null) {
379  $html = $this->_highlight_html(htmlspecialchars($this->author()), $search);
380  $a[] = ' <div class="author">'.($add_link
381  ? '<a href="./?author='.rawurlencode($this->author()).'"'.$link_target.'>'.$html.'</a>'
382  : $html).'</div>';
383  }
384 
385  if ($this->work() !== null) {
386  $html = $this->_highlight_html(htmlspecialchars($this->work()), $search);
387  $a[] = ' <div class="work">'.($add_link
388  ? '<a href="./?work='.rawurlencode($this->work()).'"'.$link_target.'>'.$html.'</a>'
389  : $html).'</div>';
390  }
391  }
392 
393  $a[] = ' </footer>';
394  $a[] = '</section>
395 ';
396 
397  return implode('
398 ', $a);
399  }
400 
401 
402  /**
403  * \brief
404  * Return the quotation/maxim with its author/nation and work
405  * in text format
406  * (strip HTML tags &lt;b>, &lt;c>, &lt;i>, &lt;sub>, &lt;sup>
407  * and special character ¨ (diaeresis, U+00A8)).
408  * @return string
409  */
410  public function to_text() {
411  $quot = trim($this->text());
412 
413  $quot = preg_replace('/<(b|c|i|sub|sup|\/b|\/c|\/i|\/sub|\/sup)>/', '', $quot); // strip some HTML tags
414  $quot = preg_replace('/¨/', '', $quot); // strip special character ¨
415 
416  $quot = '"'.$quot.'"';
417 
418  if ($this->is_maxim()) {
419  if ($this->nation() !== null) { // add nation
420  $quot .= '
421 (nation '.$this->nation().')';
422  }
423  }
424  else {
425  if ($this->author() !== null) {
426  if ($this->work() !== null) { // add author and work
427  $quot .= '
428 ('.$this->author().'/
429 '.$this->work().')';
430  }
431  else { // add author
432  $quot .= '
433 ('.$this->author().')';
434  }
435  }
436  else if ($this->work() !== null) { // add work
437  $quot .= '
438 ('.$this->work().')';
439  }
440  }
441 
442  return $quot;
443  }
444 
445 
446  /**
447  * \brief
448  * Return the quotation/maxim with its author/nation and work
449  * in text format to post to Facebook.
450  *
451  * If url !== ''
452  * then add url to the end on a newline.
453  *
454  * @param string $url
455  *
456  * @return string
457  */
458  public function to_text_facebook($url='') {
459  #DEBUG
460  assert('is_string($url)');
461  #DEBUG_END
462 
463  $quot = $this->to_text();
464 
465  if ($url !== '') {
466  $url = '
467 '.$url;
468  }
469 
470  return $quot.$url;
471  }
472 
473 
474  /**
475  * \brief
476  * Return the quotation/maxim with its author/nation and work
477  * in short text format to post to Twitter.
478  *
479  * If a cutting is required then reduce contiguous whitespaces.
480  *
481  * Cut the result to not be longer than $max_length characters.
482  * See
483  * https://dev.twitter.com/docs/tco-link-wrapper/faq#How_do_I_calculate_if_a_Tweet_with_a_link_is_going_to_be_over_140_characters_or_not
484  *
485  * If url !== null
486  * then add url to the end on a newline.
487  *
488  * If $short_url_length === null
489  * then use the real length of the url,
490  * else assume that the url will be replaced by a short url not be longer than $short_url_length.
491  *
492  * @param null|string $url
493  * @param null|int $short_url_length
494  * @param int $max_length (must be > 0)
495  *
496  * @return string
497  */
498  public function to_text_twitter($url=null, $short_url_length=null, $max_length=140) {
499  #DEBUG
500  assert('($url === null) || is_string($url)');
501  assert('($short_url_length === null) || is_int($short_url_length)');
502  assert('($short_url_length === null) || ($short_url_length >= 0)');
503  assert('is_int($max_length)');
504  assert('$max_length > 0');
505  #DEBUG_END
506 
507  $quot = $this->to_text();
508 
509  if ($url !== null) {
510  if ($short_url_length === null) {
511  $short_url_length = mb_strlen($url);
512  }
514 
515  $url = '
516 '.$url;
517  }
518 
519  return text_cut($quot, $max_length).$url;
520  }
521 
522 
523  /**
524  * \brief
525  * Return null or the translation of the quotation/maxim.
526  *
527  * @return null|string
528  */
529  public function translation() {
530  return $this->translation;
531  }
532 
533 
534  /**
535  * \brief
536  * Return null or the work of the quotation.
537  *
538  * @return null|string
539  */
540  public function work() {
541  return ($this->maxim
542  ? null
543  : $this->work);
544  }
545 
546 
547 
548  /** @var null|string $author
549  * \brief
550  * null or the author of the quotation if is a quotation,
551  * else null.
552  */
553  protected $author;
554 
555  /** @var int $id
556  * \brief
557  * The id of the quotation/maxim (must be > 0).
558  */
559  protected $id;
560 
561  /** @var bool $is_marked
562  * \brief
563  * true if marked quotation/maxim,
564  * else false.
565  */
566  protected $is_marked;
567 
568  /** @var bool $is_maxim
569  * \brief
570  * true if maxim (of a possibly nation),
571  * false if quotation (of an possibly author)
572  */
573  protected $is_maxim;
574 
575  /** @var null|string $nation
576  * \brief
577  * null or the nation of the maxim if is a maxim,
578  * else null.
579  */
580  protected $nation;
581 
582  /** @var null|string $subject
583  * \brief
584  * null or the subject of the quotation/maxim.
585  */
586  protected $subject;
587 
588  /**
589  * @var string $text
590  * \brief
591  * Text of the quotation/maxim.
592  *
593  * Maybe contains some HTML tags (see __construct()).
594  */
595  protected $text;
596 
597  /**
598  * @var null|string $translation
599  * \brief
600  * null or the translation of the quotation/maxim.
601  *
602  * Maybe contains some HTML tags (see __construct()).
603  */
604  protected $translation;
605 
606  /** @var string null|$work
607  * \brief
608  * null or the work of the quotation if is a quotation,
609  * else null.
610  */
611  protected $work;
612 }
613 
614 
615 return TRUE;
616 
617 ?>