dolibarr  13.0.2
rssparser.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2011-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3  *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
27 class RssParser
28 {
32  public $db;
33 
37  public $error = '';
38 
39  private $_format = '';
40  private $_urlRSS;
41  private $_language;
42  private $_generator;
43  private $_copyright;
44  private $_lastbuilddate;
45  private $_imageurl;
46  private $_link;
47  private $_title;
48  private $_description;
49  private $_lastfetchdate; // Last successful fetch
50  private $_rssarray = array();
51 
52  // For parsing with xmlparser
53  public $stack = array(); // parser stack
54  private $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
55 
56 
62  public function __construct($db)
63  {
64  $this->db = $db;
65  }
66 
72  public function getFormat()
73  {
74  return $this->_format;
75  }
76 
82  public function getUrlRss()
83  {
84  return $this->_urlRSS;
85  }
91  public function getLanguage()
92  {
93  return $this->_language;
94  }
100  public function getGenerator()
101  {
102  return $this->_generator;
103  }
109  public function getCopyright()
110  {
111  return $this->_copyright;
112  }
118  public function getLastBuildDate()
119  {
120  return $this->_lastbuilddate;
121  }
127  public function getImageUrl()
128  {
129  return $this->_imageurl;
130  }
136  public function getLink()
137  {
138  return $this->_link;
139  }
145  public function getTitle()
146  {
147  return $this->_title;
148  }
154  public function getDescription()
155  {
156  return $this->_description;
157  }
163  public function getLastFetchDate()
164  {
165  return $this->_lastfetchdate;
166  }
172  public function getItems()
173  {
174  return $this->_rssarray;
175  }
176 
177 
187  public function parser($urlRSS, $maxNb = 0, $cachedelay = 60, $cachedir = '')
188  {
189  global $conf;
190 
191  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
192 
193  $rss = '';
194  $str = ''; // This will contain content of feed
195 
196  // Check parameters
197  if (!dol_is_url($urlRSS))
198  {
199  $this->error = "ErrorBadUrl";
200  return -1;
201  }
202 
203  $this->_urlRSS = $urlRSS;
204  $newpathofdestfile = $cachedir.'/'.dol_hash($this->_urlRSS, 3); // Force md5 hash (does not contains special chars)
205  $newmask = '0644';
206 
207  //dol_syslog("RssPArser::parser parse url=".$urlRSS." => cache file=".$newpathofdestfile);
208  $nowgmt = dol_now();
209 
210  // Search into cache
211  $foundintocache = 0;
212  if ($cachedelay > 0 && $cachedir)
213  {
214  $filedate = dol_filemtime($newpathofdestfile);
215  if ($filedate >= ($nowgmt - $cachedelay))
216  {
217  //dol_syslog("RssParser::parser cache file ".$newpathofdestfile." is not older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we use it.");
218  $foundintocache = 1;
219 
220  $this->_lastfetchdate = $filedate;
221  } else {
222  dol_syslog(get_class($this)."::parser cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
223  }
224  }
225 
226  // Load file into $str
227  if ($foundintocache) // Cache file found and is not too old
228  {
229  $str = file_get_contents($newpathofdestfile);
230  } else {
231  try {
232  ini_set("user_agent", "Dolibarr ERP-CRM RSS reader");
233  ini_set("max_execution_time", $conf->global->MAIN_USE_RESPONSE_TIMEOUT);
234  ini_set("default_socket_timeout", $conf->global->MAIN_USE_RESPONSE_TIMEOUT);
235 
236  $opts = array('http'=>array('method'=>"GET"));
237  if (!empty($conf->global->MAIN_USE_CONNECT_TIMEOUT)) $opts['http']['timeout'] = $conf->global->MAIN_USE_CONNECT_TIMEOUT;
238  if (!empty($conf->global->MAIN_PROXY_USE)) $opts['http']['proxy'] = 'tcp://'.$conf->global->MAIN_PROXY_HOST.':'.$conf->global->MAIN_PROXY_PORT;
239  //var_dump($opts);exit;
240  $context = stream_context_create($opts);
241 
242  $str = file_get_contents($this->_urlRSS, false, $context);
243  } catch (Exception $e) {
244  print 'Error retrieving URL '.$this->_urlRSS.' - '.$e->getMessage();
245  }
246  }
247 
248  if ($str !== false)
249  {
250  // Convert $str into xml
251  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML))
252  {
253  //print 'xx'.LIBXML_NOCDATA;
254  libxml_use_internal_errors(false);
255  $rss = simplexml_load_string($str, "SimpleXMLElement", LIBXML_NOCDATA);
256  } else {
257  if (!function_exists('xml_parser_create')) {
258  $this->error = 'Function xml_parser_create are not supported by your PHP';
259  return -1;
260  }
261 
262  $xmlparser = xml_parser_create('');
263  if (!is_resource($xmlparser)) {
264  $this->error = "ErrorFailedToCreateParser";
265  return -1;
266  }
267 
268  xml_set_object($xmlparser, $this);
269  xml_set_element_handler($xmlparser, 'feed_start_element', 'feed_end_element');
270  xml_set_character_data_handler($xmlparser, 'feed_cdata');
271  $status = xml_parse($xmlparser, $str);
272  xml_parser_free($xmlparser);
273  $rss = $this;
274  //var_dump($rss->_format);exit;
275  }
276  }
277 
278  // If $rss loaded
279  if ($rss)
280  {
281  // Save file into cache
282  if (empty($foundintocache) && $cachedir)
283  {
284  dol_syslog(get_class($this)."::parser cache file ".$newpathofdestfile." is saved onto disk.");
285  if (!dol_is_dir($cachedir)) dol_mkdir($cachedir);
286  $fp = fopen($newpathofdestfile, 'w');
287  if ($fp)
288  {
289  fwrite($fp, $str);
290  fclose($fp);
291  if (!empty($conf->global->MAIN_UMASK)) $newmask = $conf->global->MAIN_UMASK;
292  @chmod($newpathofdestfile, octdec($newmask));
293 
294  $this->_lastfetchdate = $nowgmt;
295  } else {
296  print 'Error, failed to open file '.$newpathofdestfile.' for write';
297  }
298  }
299 
300  unset($str); // Free memory
301 
302  if (empty($rss->_format)) // If format not detected automatically
303  {
304  $rss->_format = 'rss';
305  if (empty($rss->channel)) $rss->_format = 'atom';
306  }
307 
308  $items = array();
309 
310  // Save description entries
311  if ($rss->_format == 'rss') {
312  //var_dump($rss);
313  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
314  if (!empty($rss->channel->language)) $this->_language = (string) $rss->channel->language;
315  if (!empty($rss->channel->generator)) $this->_generator = (string) $rss->channel->generator;
316  if (!empty($rss->channel->copyright)) $this->_copyright = (string) $rss->channel->copyright;
317  if (!empty($rss->channel->lastbuilddate)) $this->_lastbuilddate = (string) $rss->channel->lastbuilddate;
318  if (!empty($rss->channel->image->url[0])) $this->_imageurl = (string) $rss->channel->image->url[0];
319  if (!empty($rss->channel->link)) $this->_link = (string) $rss->channel->link;
320  if (!empty($rss->channel->title)) $this->_title = (string) $rss->channel->title;
321  if (!empty($rss->channel->description)) $this->_description = (string) $rss->channel->description;
322  } else {
323  //var_dump($rss->channel);
324  if (!empty($rss->channel['language'])) $this->_language = (string) $rss->channel['language'];
325  if (!empty($rss->channel['generator'])) $this->_generator = (string) $rss->channel['generator'];
326  if (!empty($rss->channel['copyright'])) $this->_copyright = (string) $rss->channel['copyright'];
327  if (!empty($rss->channel['lastbuilddate'])) $this->_lastbuilddate = (string) $rss->channel['lastbuilddate'];
328  if (!empty($rss->image['url'])) $this->_imageurl = (string) $rss->image['url'];
329  if (!empty($rss->channel['link'])) $this->_link = (string) $rss->channel['link'];
330  if (!empty($rss->channel['title'])) $this->_title = (string) $rss->channel['title'];
331  if (!empty($rss->channel['description'])) $this->_description = (string) $rss->channel['description'];
332  }
333 
334  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) $items = $rss->channel->item; // With simplexml
335  else $items = $rss->items; // With xmlparse
336  //var_dump($items);exit;
337  } elseif ($rss->_format == 'atom') {
338  //var_dump($rss);
339  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML))
340  {
341  if (!empty($rss->generator)) $this->_generator = (string) $rss->generator;
342  if (!empty($rss->lastbuilddate)) $this->_lastbuilddate = (string) $rss->modified;
343  if (!empty($rss->link->href)) $this->_link = (string) $rss->link->href;
344  if (!empty($rss->title)) $this->_title = (string) $rss->title;
345  if (!empty($rss->description)) $this->_description = (string) $rss->description;
346  } else {
347  //if (!empty($rss->channel['rss_language'])) $this->_language = (string) $rss->channel['rss_language'];
348  if (!empty($rss->channel['generator'])) $this->_generator = (string) $rss->channel['generator'];
349  //if (!empty($rss->channel['rss_copyright'])) $this->_copyright = (string) $rss->channel['rss_copyright'];
350  if (!empty($rss->channel['modified'])) $this->_lastbuilddate = (string) $rss->channel['modified'];
351  //if (!empty($rss->image['rss_url'])) $this->_imageurl = (string) $rss->image['rss_url'];
352  if (!empty($rss->channel['link'])) $this->_link = (string) $rss->channel['link'];
353  if (!empty($rss->channel['title'])) $this->_title = (string) $rss->channel['title'];
354  //if (!empty($rss->channel['rss_description'])) $this->_description = (string) $rss->channel['rss_description'];
355 
356  if (!empty($rss->channel)) {
357  $this->_imageurl = $this->getAtomImageUrl($rss->channel);
358  }
359  }
360  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
361  $tmprss = xml2php($rss); $items = $tmprss['entry'];
362  } // With simplexml
363  else $items = $rss->items; // With xmlparse
364  //var_dump($items);exit;
365  }
366 
367  $i = 0;
368 
369  // Loop on each record
370  if (is_array($items)) {
371  foreach ($items as $item) {
372  //var_dump($item);exit;
373  if ($rss->_format == 'rss') {
374  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
375  $itemLink = (string) $item->link;
376  $itemTitle = (string) $item->title;
377  $itemDescription = (string) $item->description;
378  $itemPubDate = (string) $item->pubDate;
379  $itemId = '';
380  $itemAuthor = '';
381  } else {
382  $itemLink = (string) $item['link'];
383  $itemTitle = (string) $item['title'];
384  $itemDescription = (string) $item['description'];
385  $itemPubDate = (string) $item['pubdate'];
386  $itemId = (string) $item['guid'];
387  $itemAuthor = (string) $item['author'];
388  }
389 
390  // Loop on each category
391  $itemCategory = array();
392  if (is_array($item->category)) {
393  foreach ($item->category as $cat) {
394  $itemCategory[] = (string) $cat;
395  }
396  }
397  } elseif ($rss->_format == 'atom') {
398  if (!empty($conf->global->EXTERNALRSS_USE_SIMPLEXML)) {
399  $itemLink = (isset($item['link']) ? (string) $item['link'] : '');
400  $itemTitle = (string) $item['title'];
401  $itemDescription = $this->getAtomItemDescription($item);
402  $itemPubDate = (string) $item['created'];
403  $itemId = (string) $item['id'];
404  $itemAuthor = (string) ($item['author'] ? $item['author'] : $item['author_name']);
405  } else {
406  $itemLink = (isset($item['link']) ? (string) $item['link'] : '');
407  $itemTitle = (string) $item['title'];
408  $itemDescription = $this->getAtomItemDescription($item);
409  $itemPubDate = (string) $item['created'];
410  $itemId = (string) $item['id'];
411  $itemAuthor = (string) ($item['author'] ? $item['author'] : $item['author_name']);
412  }
413  $itemCategory = array();
414  } else {
415  $itemCategory = array();
416  $itemLink = '';
417  $itemTitle = '';
418  $itemDescription = '';
419  $itemPubDate = '';
420  $itemId = '';
421  $itemAuthor = '';
422  print 'ErrorBadFeedFormat';
423  }
424 
425  // Add record to result array
426  $this->_rssarray[$i] = array(
427  'link'=>$itemLink,
428  'title'=>$itemTitle,
429  'description'=>$itemDescription,
430  'pubDate'=>$itemPubDate,
431  'category'=>$itemCategory,
432  'id'=>$itemId,
433  'author'=>$itemAuthor
434  );
435  //var_dump($this->_rssarray);
436 
437  $i++;
438 
439  if ($i > $maxNb) break; // We get all records we want
440  }
441  }
442 
443  return 1;
444  } else {
445  $this->error = 'ErrorFailedToLoadRSSFile';
446  return -1;
447  }
448  }
449 
450 
451 
452  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
461  public function feed_start_element($p, $element, &$attrs)
462  {
463  // phpcs:enable
464  $el = $element = strtolower($element);
465  $attrs = array_change_key_case($attrs, CASE_LOWER);
466 
467  // check for a namespace, and split if found
468  $ns = false;
469  if (strpos($element, ':'))
470  {
471  list($ns, $el) = explode(':', $element, 2);
472  }
473  if ($ns and $ns != 'rdf')
474  {
475  $this->current_namespace = $ns;
476  }
477 
478  // if feed type isn't set, then this is first element of feed identify feed from root element
479  if (empty($this->_format))
480  {
481  if ($el == 'rdf') {
482  $this->_format = 'rss';
483  $this->feed_version = '1.0';
484  } elseif ($el == 'rss') {
485  $this->_format = 'rss';
486  $this->feed_version = $attrs['version'];
487  } elseif ($el == 'feed') {
488  $this->_format = 'atom';
489  $this->feed_version = $attrs['version'];
490  $this->inchannel = true;
491  }
492  return;
493  }
494 
495  if ($el == 'channel')
496  {
497  $this->inchannel = true;
498  } elseif ($el == 'item' or $el == 'entry')
499  {
500  $this->initem = true;
501  if (isset($attrs['rdf:about'])) {
502  $this->current_item['about'] = $attrs['rdf:about'];
503  }
504  }
505 
506  // if we're in the default namespace of an RSS feed,
507  // record textinput or image fields
508  elseif (
509  $this->_format == 'rss' and
510  $this->current_namespace == '' and
511  $el == 'textinput' )
512  {
513  $this->intextinput = true;
514  } elseif (
515  $this->_format == 'rss' and
516  $this->current_namespace == '' and
517  $el == 'image' )
518  {
519  $this->inimage = true;
520  }
521 
522  // handle atom content constructs
523  elseif ($this->_format == 'atom' and in_array($el, $this->_CONTENT_CONSTRUCTS))
524  {
525  // avoid clashing w/ RSS mod_content
526  if ($el == 'content') {
527  $el = 'atom_content';
528  }
529 
530  $this->incontent = $el;
531  }
532 
533  // if inside an Atom content construct (e.g. content or summary) field treat tags as text
534  elseif ($this->_format == 'atom' and $this->incontent)
535  {
536  // if tags are inlined, then flatten
537  $attrs_str = join(' ', array_map('map_attrs', array_keys($attrs), array_values($attrs)));
538 
539  $this->append_content("<$element $attrs_str>");
540 
541  array_unshift($this->stack, $el);
542  }
543 
544  // Atom support many links per containging element.
545  // Magpie treats link elements of type rel='alternate'
546  // as being equivalent to RSS's simple link element.
547  //
548  elseif ($this->_format == 'atom' and $el == 'link')
549  {
550  if (isset($attrs['rel']) && $attrs['rel'] == 'alternate')
551  {
552  $link_el = 'link';
553  } elseif (!isset($attrs['rel']))
554  {
555  $link_el = 'link';
556  } else {
557  $link_el = 'link_'.$attrs['rel'];
558  }
559 
560  $this->append($link_el, $attrs['href']);
561  }
562  // set stack[0] to current element
563  else {
564  array_unshift($this->stack, $el);
565  }
566  }
567 
568 
569  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
577  public function feed_cdata($p, $text)
578  {
579  // phpcs:enable
580  if ($this->_format == 'atom' and $this->incontent)
581  {
582  $this->append_content($text);
583  } else {
584  $current_el = join('_', array_reverse($this->stack));
585  $this->append($current_el, $text);
586  }
587  }
588 
589  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
597  public function feed_end_element($p, $el)
598  {
599  // phpcs:enable
600  $el = strtolower($el);
601 
602  if ($el == 'item' or $el == 'entry')
603  {
604  $this->items[] = $this->current_item;
605  $this->current_item = array();
606  $this->initem = false;
607  } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'textinput')
608  {
609  $this->intextinput = false;
610  } elseif ($this->_format == 'rss' and $this->current_namespace == '' and $el == 'image')
611  {
612  $this->inimage = false;
613  } elseif ($this->_format == 'atom' and in_array($el, $this->_CONTENT_CONSTRUCTS))
614  {
615  $this->incontent = false;
616  } elseif ($el == 'channel' or $el == 'feed')
617  {
618  $this->inchannel = false;
619  } elseif ($this->_format == 'atom' and $this->incontent) {
620  // balance tags properly
621  // note: i don't think this is actually neccessary
622  if ($this->stack[0] == $el)
623  {
624  $this->append_content("</$el>");
625  } else {
626  $this->append_content("<$el />");
627  }
628 
629  array_shift($this->stack);
630  } else {
631  array_shift($this->stack);
632  }
633 
634  $this->current_namespace = false;
635  }
636 
637 
645  public function concat(&$str1, $str2 = "")
646  {
647  if (!isset($str1)) {
648  $str1 = "";
649  }
650  $str1 .= $str2;
651  }
652 
653  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
660  public function append_content($text)
661  {
662  // phpcs:enable
663  if ($this->initem) {
664  $this->concat($this->current_item[$this->incontent], $text);
665  } elseif ($this->inchannel) {
666  $this->concat($this->channel[$this->incontent], $text);
667  }
668  }
669 
677  public function append($el, $text)
678  {
679  if (!$el) {
680  return;
681  }
682  if ($this->current_namespace)
683  {
684  if ($this->initem) {
685  $this->concat($this->current_item[$this->current_namespace][$el], $text);
686  } elseif ($this->inchannel) {
687  $this->concat($this->channel[$this->current_namespace][$el], $text);
688  } elseif ($this->intextinput) {
689  $this->concat($this->textinput[$this->current_namespace][$el], $text);
690  } elseif ($this->inimage) {
691  $this->concat($this->image[$this->current_namespace][$el], $text);
692  }
693  } else {
694  if ($this->initem) {
695  $this->concat($this->current_item[$el], $text);
696  } elseif ($this->intextinput) {
697  $this->concat($this->textinput[$el], $text);
698  } elseif ($this->inimage) {
699  $this->concat($this->image[$el], $text);
700  } elseif ($this->inchannel) {
701  $this->concat($this->channel[$el], $text);
702  }
703  }
704  }
705 
713  private function getAtomItemDescription(array $item, $maxlength = 500)
714  {
715  $result = "";
716 
717  if (isset($item['summary']))
718  {
719  $result = $item['summary'];
720  } elseif (isset($item['atom_content']))
721  {
722  $result = $item['atom_content'];
723  }
724 
725  // remove all HTML elements that can possible break the maximum size of a tooltip,
726  // like headings, image, video etc. and allow only simple style elements
727  $result = strip_tags($result, "<br><p><ul><ol><li>");
728 
729  $result = str_replace("\n", "", $result);
730 
731  if (strlen($result) > $maxlength)
732  {
733  $result = substr($result, 0, $maxlength);
734  $result .= "...";
735  }
736 
737  return $result;
738  }
739 
746  private function getAtomImageUrl(array $feed)
747  {
748  if (isset($feed['icon']))
749  {
750  return $feed['logo'];
751  }
752 
753  if (isset($feed['icon']))
754  {
755  return $feed['logo'];
756  }
757 
758  if (isset($feed['webfeeds:logo']))
759  {
760  return $feed['webfeeds:logo'];
761  }
762 
763  if (isset($feed['webfeeds:icon']))
764  {
765  return $feed['webfeeds:icon'];
766  }
767 
768  if (isset($feed['webfeeds:wordmark']))
769  {
770  return $feed['webfeeds:wordmark'];
771  }
772 
773  return "";
774  }
775 }
776 
777 
784 function xml2php($xml)
785 {
786  $fils = 0;
787  $tab = false;
788  $array = array();
789  foreach ($xml->children() as $key => $value)
790  {
791  $child = xml2php($value);
792 
793  //To deal with the attributes
794  foreach ($value->attributes() as $ak=>$av)
795  {
796  $child[$ak] = (string) $av;
797  }
798 
799  //Let see if the new child is not in the array
800  if ($tab === false && in_array($key, array_keys($array)))
801  {
802  //If this element is already in the array we will create an indexed array
803  $tmp = $array[$key];
804  $array[$key] = null;
805  $array[$key][] = $tmp;
806  $array[$key][] = $child;
807  $tab = true;
808  } elseif ($tab === true)
809  {
810  //Add an element in an existing array
811  $array[$key][] = $child;
812  } else {
813  //Add a simple element
814  $array[$key] = $child;
815  }
816 
817  $fils++;
818  }
819 
820 
821  if ($fils == 0)
822  {
823  return (string) $xml;
824  }
825 
826  return $array;
827 }
dol_hash($chain, $type= '0')
Returns a hash of a string.
getUrlRss()
getUrlRss
feed_start_element($p, $element, &$attrs)
Triggered when opened tag is found.
parser($urlRSS, $maxNb=0, $cachedelay=60, $cachedir= '')
Parse rss URL.
dol_now($mode= 'auto')
Return date for now.
concat(&$str1, $str2="")
To concat 2 string with no warning if an operand is not defined.
getItems()
getItems
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:432
getFormat()
getFormat
$conf db
API class for accounts.
Definition: inc.php:54
getAtomItemDescription(array $item, $maxlength=500)
Return a description/summary for one item from a ATOM feed.
append($el, $text)
smart append - field and namespace aware
feed_end_element($p, $el)
Triggered when closed tag is found.
dol_is_url($url)
Return if path is an URL.
Definition: files.lib.php:481
getDescription()
getDescription
getLink()
getLink
xml2php($xml)
Function to convert an XML object into an array.
getCopyright()
getCopyright
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
append_content($text)
Enter description here ...
getAtomImageUrl(array $feed)
Return a URL to a image of the given ATOM feed.
__construct($db)
Constructor.
getLanguage()
getLanguage
getImageUrl()
getImageUrl
print
Draft customers invoices.
Definition: index.php:89
getTitle()
getTitle
dol_filemtime($pathoffile)
Return time of a file.
Definition: files.lib.php:567
getGenerator()
getGenerator
feed_cdata($p, $text)
Triggered when CDATA is found.
getLastFetchDate()
getLastFetchDate
getLastBuildDate()
getLastBuildDate
Class to parse RSS files.
if(!defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN'
Draft customers invoices.
dol_mkdir($dir, $dataroot= '', $newmask=null)
Creation of a directory (this can create recursive subdir)