Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [Versions 402 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle 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  // Moodle 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 Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This plugin is used to access flickr pictures
  19   *
  20   * @since Moodle 2.0
  21   * @package    repository_flickr_public
  22   * @copyright  2010 Dongsheng Cai {@link http://dongsheng.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  require_once($CFG->dirroot . '/repository/lib.php');
  26  require_once($CFG->libdir.'/flickrlib.php');
  27  require_once (__DIR__ . '/image.php');
  28  
  29  /**
  30   * repository_flickr_public class
  31   * This one is used to create public repository
  32   * You can set up a public account in admin page, so everyone can access
  33   * flickr photos from this plugin
  34   *
  35   * @since Moodle 2.0
  36   * @package    repository_flickr_public
  37   * @copyright  2009 Dongsheng Cai {@link http://dongsheng.org}
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class repository_flickr_public extends repository {
  41  
  42      /** @var phpFlickr Flickr class. */
  43      private $flickr;
  44  
  45      /** @var string Flick photos. */
  46      public $photos;
  47  
  48      /** @var string API key. */
  49      protected $api_key;
  50  
  51      /** @var string email address account. */
  52      protected $flickr_account;
  53  
  54      /** @var string watermarks usage status. */
  55      protected $usewatermarks;
  56  
  57      /** @var string session account. */
  58      protected $sess_account;
  59  
  60      /** @var string session tag. */
  61      protected $sess_tag;
  62  
  63      /** @var string session text. */
  64      protected $sess_text;
  65  
  66      /** @var string Flickr user identifier. */
  67      protected $nsid;
  68  
  69      /**
  70       * Stores sizes of images to prevent multiple API call
  71       */
  72      static private $sizes = array();
  73  
  74      /**
  75       * constructor method
  76       *
  77       * @global object $CFG
  78       * @global object $SESSION
  79       * @param int $repositoryid
  80       * @param int $context
  81       * @param array $options
  82       * @param boolean $readonly
  83       */
  84      public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array(), $readonly=0) {
  85          global $CFG, $SESSION;
  86          parent::__construct($repositoryid, $context, $options,$readonly);
  87          $this->api_key = $this->get_option('api_key');
  88          $this->flickr  = new phpFlickr($this->api_key);
  89          $this->flickr_account = $this->get_option('email_address');
  90          $this->usewatermarks = $this->get_option('usewatermarks');
  91  
  92          $account  = optional_param('flickr_account', '', PARAM_RAW);
  93          $fulltext = optional_param('flickr_fulltext', '', PARAM_RAW);
  94          if (empty($fulltext)) {
  95              $fulltext = optional_param('s', '', PARAM_RAW);
  96          }
  97          $tag      = optional_param('flickr_tag', '', PARAM_RAW);
  98          $license  = optional_param('flickr_license', '', PARAM_RAW);
  99  
 100          $this->sess_account = 'flickr_public_'.$this->id.'_account';
 101          $this->sess_tag     = 'flickr_public_'.$this->id.'_tag';
 102          $this->sess_text    = 'flickr_public_'.$this->id.'_text';
 103  
 104          if (!empty($account) or !empty($fulltext) or !empty($tag) or !empty($license)) {
 105              $SESSION->{$this->sess_tag}  = $tag;
 106              $SESSION->{$this->sess_text} = $fulltext;
 107              $SESSION->{$this->sess_account} = $account;
 108          }
 109      }
 110  
 111      /**
 112       * save api_key in config table
 113       * @param array $options
 114       * @return boolean
 115       */
 116      public function set_option($options = array()) {
 117          if (!empty($options['api_key'])) {
 118              set_config('api_key', trim($options['api_key']), 'flickr_public');
 119          }
 120          unset($options['api_key']);
 121          return parent::set_option($options);
 122      }
 123  
 124      /**
 125       * get api_key from config table
 126       *
 127       * @param string $config
 128       * @return mixed
 129       */
 130      public function get_option($config = '') {
 131          if ($config==='api_key') {
 132              return trim(get_config('flickr_public', 'api_key'));
 133          } else {
 134              $options['api_key'] = trim(get_config('flickr_public', 'api_key'));
 135          }
 136          return parent::get_option($config);
 137      }
 138  
 139      /**
 140       * is global_search available?
 141       *
 142       * @return boolean
 143       */
 144      public function global_search() {
 145          if (empty($this->flickr_account)) {
 146              return false;
 147          } else {
 148              return true;
 149          }
 150      }
 151  
 152      /**
 153       * check if flickr account
 154       * @return boolean
 155       */
 156      public function check_login() {
 157          return !empty($this->flickr_account);
 158      }
 159  
 160      /**
 161       * construct login form
 162       *
 163       * @param boolean $ajax
 164       * @return array
 165       */
 166      public function print_login() {
 167          if ($this->options['ajax']) {
 168              $ret = array();
 169              $fulltext = new stdClass();
 170              $fulltext->label = get_string('fulltext', 'repository_flickr_public').': ';
 171              $fulltext->id    = 'el_fulltext';
 172              $fulltext->type = 'text';
 173              $fulltext->name = 'flickr_fulltext';
 174  
 175              $tag = new stdClass();
 176              $tag->label = get_string('tag', 'repository_flickr_public').': ';
 177              $tag->id    = 'el_tag';
 178              $tag->type = 'text';
 179              $tag->name = 'flickr_tag';
 180  
 181              $email_field = new stdClass();
 182              $email_field->label = get_string('username', 'repository_flickr_public').': ';
 183              $email_field->id    = 'account';
 184              $email_field->type = 'text';
 185              $email_field->name = 'flickr_account';
 186  
 187              $commercial = new stdClass();
 188              $commercial->label = get_string('commercialuse', 'repository_flickr_public').': ';
 189              $commercial->id    = 'flickr_commercial_id';
 190              $commercial->type  = 'checkbox';
 191              $commercial->name  = 'flickr_commercial';
 192              $commercial->value = 'yes';
 193  
 194              $modification = new stdClass();
 195              $modification->label = get_string('modification', 'repository_flickr_public').': ';
 196              $modification->id    = 'flickr_modification_id';
 197              $modification->type  = 'checkbox';
 198              $modification->name  = 'flickr_modification';
 199              $modification->value = 'yes';
 200  
 201              $ret['login'] = array($fulltext, $tag, $email_field, $commercial, $modification);
 202              $ret['login_btn_label'] = get_string('search');
 203              $ret['login_btn_action'] = 'search';
 204              return $ret;
 205          } else {
 206              echo '<table>';
 207              echo '<tr><td><label>'.get_string('fulltext', 'repository_flickr_public').'</label></td>';
 208              echo '<td><input type="text" name="flickr_fulltext" /></td></tr>';
 209              echo '<tr><td><label>'.get_string('tag', 'repository_flickr_public').'</label></td>';
 210              echo '<td><input type="text" name="flickr_tag" /></td></tr>';
 211              echo '<tr><td><label>'.get_string('username', 'repository_flickr_public').'</label></td>';
 212              echo '<td><input type="text" name="flickr_account" /></td></tr>';
 213  
 214              echo '<tr><td><label>'.get_string('commercialuse', 'repository_flickr_public').'</label></td>';
 215              echo '<td>';
 216              echo '<input type="checkbox" name="flickr_commercial" value="yes" />';
 217              echo '</td></tr>';
 218  
 219              echo '<tr><td><label>'.get_string('modification', 'repository_flickr_public').'</label></td>';
 220              echo '<td>';
 221              echo '<input type="checkbox" name="flickr_modification" value="yes" />';
 222              echo '</td></tr>';
 223  
 224              echo '</table>';
 225  
 226              echo '<input type="hidden" name="action" value="search" />';
 227              echo '<input type="submit" value="'.get_string('search', 'repository').'" />';
 228          }
 229      }
 230  
 231      /**
 232       * destroy session
 233       *
 234       * @return object
 235       */
 236      public function logout() {
 237          global $SESSION;
 238          unset($SESSION->{$this->sess_tag});
 239          unset($SESSION->{$this->sess_text});
 240          unset($SESSION->{$this->sess_account});
 241          return $this->print_login();
 242      }
 243  
 244      public function license4moodle ($license_id) {
 245          $license = array(
 246              '0' => 'allrightsreserved',
 247              '1' => 'cc-nc-sa',
 248              '2' => 'cc-nc',
 249              '3' => 'cc-nc-nd',
 250              '4' => 'cc',
 251              '5' => 'cc-sa',
 252              '6' => 'cc-nd',
 253              '7' => 'other'
 254              );
 255          return $license[$license_id];
 256      }
 257  
 258      /**
 259       * search images on flickr
 260       *
 261       * @param string $search_text
 262       * @return array
 263       */
 264      public function search($search_text, $page = 0) {
 265          global $SESSION;
 266          $ret = array();
 267          if (empty($page)) {
 268              $page = 1;
 269          }
 270  
 271          if (!empty($this->flickr_account)) {
 272              $people = $this->flickr->people_findByEmail($this->flickr_account);
 273              $this->nsid = $people['nsid'];
 274          }
 275          if (!empty($SESSION->{$this->sess_account})) {
 276              $people = $this->flickr->people_findByEmail($SESSION->{$this->sess_account});
 277              $this->nsid = $people['nsid'];
 278          }
 279          if (empty($this->nsid)) {
 280              $this->nsid = null;
 281              // user specify a flickr account, but it is not valid
 282              if (!empty($this->flickr_account) or !empty($SESSION->{$this->sess_account})) {
 283                  $ret['e'] = get_string('invalidemail', 'repository_flickr_public');
 284                  return $ret;
 285              }
 286          }
 287  
 288          // including all licenses by default
 289          $licenses = array(1=>1, 2, 3, 4, 5, 6, 7);
 290  
 291          $commercial   = optional_param('flickr_commercial', '', PARAM_RAW);
 292          $modification = optional_param('flickr_modification', '', PARAM_RAW);
 293  
 294          if ($commercial == 'yes') {
 295              // including
 296              // 4: Attribution License
 297              // 5: Attribution ShareAlike
 298              // 6: Attribution NoDerives
 299              // 7: unknown license
 300              unset($licenses[1], $licenses[2], $licenses[3]);
 301          }
 302          if ($modification == 'yes') {
 303              // including
 304              // 1: Attribution NonCommercial ShareAlike
 305              // 2: Attribution NonCommercial
 306              // 4: Attribution License
 307              // 5: Attribution ShareAlike
 308              // 7: unknown license
 309              unset($licenses[3], $licenses[6]);
 310          }
 311          //if ($modification == 'sharealike') {
 312              // including
 313              // 1: Attribution NonCommercial ShareAlike
 314              // 5: Attribution ShareAlike
 315              //unset($licenses[2], $licenses[3], $licenses[4], $licenses[6], $licenses[7]);
 316          //}
 317  
 318          $licenses = implode(',', $licenses);
 319  
 320          $tag  = !empty($SESSION->{$this->sess_tag})  ? $SESSION->{$this->sess_tag}  : null;
 321          $text = !empty($SESSION->{$this->sess_text}) ? $SESSION->{$this->sess_text} : null;
 322          $nsid = !empty($this->nsid) ? $this->nsid : null;
 323  
 324          $photos = $this->flickr->photos_search(
 325              array(
 326                  'tags' => $tag,
 327                  'page' => $page,
 328                  'per_page' => 24,
 329                  'user_id' => $nsid,
 330                  'license' => $licenses,
 331                  'text' => $text,
 332                  'extras' => 'original_format,license,date_upload,last_update',
 333                  'media' => 'photos'
 334              )
 335          );
 336          $ret['total'] = $photos['total'];
 337          $ret['perpage'] = $photos['perpage'];
 338          if (empty($photos)) {
 339              $ret['list'] = array();
 340              return $ret;
 341          }
 342          $ret = $this->build_list($photos, $page, $ret);
 343          $ret['list'] = array_filter($ret['list'], array($this, 'filter'));
 344          return $ret;
 345      }
 346  
 347      /**
 348       * return an image list
 349       *
 350       * @param string $path
 351       * @param int $page
 352       * @return array
 353       */
 354      public function get_listing($path = '', $page = 1) {
 355          $people = $this->flickr->people_findByEmail($this->flickr_account);
 356          $this->nsid = $people['nsid'];
 357          $photos = $this->flickr->people_getPublicPhotos($people['nsid'], 'original_format,license,date_upload,last_update',
 358              24, $page);
 359          $ret = array();
 360  
 361          return $this->build_list($photos, $page, $ret);
 362      }
 363  
 364      /**
 365       * build an image list
 366       *
 367       * @param array $photos
 368       * @param int $page
 369       * @return array
 370       */
 371      private function build_list($photos, $page, &$ret) {
 372          global $OUTPUT;
 373  
 374          if (!empty($this->nsid)) {
 375              $photos_url = $this->flickr->urls_getUserPhotos($this->nsid);
 376              $ret['manage'] = $photos_url;
 377          }
 378          $ret['list']  = array();
 379          $ret['nosearch'] = true;
 380          $ret['norefresh'] = true;
 381          $ret['logouttext'] = get_string('backtosearch', 'repository_flickr_public');
 382          $ret['pages'] = $photos['pages'];
 383          if (is_int($page) && $page <= $ret['pages']) {
 384              $ret['page'] = $page;
 385          } else {
 386              $ret['page'] = 1;
 387          }
 388          if (!empty($photos['photo'])) {
 389              foreach ($photos['photo'] as $p) {
 390                  if(empty($p['title'])) {
 391                      $p['title'] = get_string('notitle', 'repository_flickr');
 392                  }
 393                  if (isset($p['originalformat'])) {
 394                      $format = $p['originalformat'];
 395                  } else {
 396                      $format = 'jpg';
 397                  }
 398                  $format = '.'.$format;
 399                  if (substr($p['title'], strlen($p['title'])-strlen($format)) != $format) {
 400                      // append author id
 401                      // $p['title'] .= '-'.$p['owner'];
 402                      // append file extension
 403                      $p['title'] .= $format;
 404                  }
 405                  // Get the thumbnail source URL.
 406                  $thumbnailsource = $this->flickr->buildPhotoURL($p, 'Square');
 407                  if (!@getimagesize($thumbnailsource)) {
 408                      // Use the file extension icon as a thumbnail if the original thumbnail does not exist to avoid
 409                      // displaying broken thumbnails in the repository.
 410                      $thumbnailsource = $OUTPUT->image_url(file_extension_icon($p['title'], 90))->out(false);
 411                  }
 412  
 413                  // Perform a HEAD request to the image to obtain it's Content-Length.
 414                  $curl = new curl();
 415                  $curl->head($this->get_link($p['id']));
 416  
 417                  // The photo sizes are statically cached, so we can retrieve image dimensions without another API call.
 418                  $bestsize = $this->get_best_size($p['id']);
 419  
 420                  $ret['list'][] = array(
 421                      'title' => $p['title'],
 422                      'source' => $p['id'],
 423                      'id' => $p['id'],
 424                      'thumbnail' => $thumbnailsource,
 425                      'datecreated' => $p['dateupload'],
 426                      'datemodified' => $p['lastupdate'],
 427                      'size' => (int)($curl->get_info()['download_content_length']),
 428                      'image_width' => $bestsize['width'],
 429                      'image_height' => $bestsize['height'],
 430                      'url' => 'http://www.flickr.com/photos/' . $p['owner'] . '/' . $p['id'],
 431                      'license' => $this->license4moodle($p['license']),
 432                      'author' => $p['owner'],
 433                  );
 434              }
 435          }
 436          return $ret;
 437      }
 438  
 439      /**
 440       * Print a search form
 441       *
 442       * @return string
 443       */
 444      public function print_search() {
 445          $str = '';
 446          $str .= '<input type="hidden" name="repo_id" value="'.$this->id.'" />';
 447          $str .= '<input type="hidden" name="ctx_id" value="'.$this->context->id.'" />';
 448          $str .= '<input type="hidden" name="seekey" value="'.sesskey().'" />';
 449          $str .= '<label>'.get_string('fulltext', 'repository_flickr_public').'</label><br/><input name="s" value="" /><br/>';
 450          $str .= '<label>'.get_string('tag', 'repository_flickr_public').'</label><br /><input type="text" name="flickr_tag" /><br />';
 451          return $str;
 452      }
 453  
 454      /**
 455       * Return photo url by given photo id
 456       * @param string $photoid
 457       * @return string
 458       */
 459      private function build_photo_url($photoid) {
 460          $bestsize = $this->get_best_size($photoid);
 461          if (!isset($bestsize['source'])) {
 462              throw new repository_exception('cannotdownload', 'repository');
 463          }
 464          return $bestsize['source'];
 465      }
 466  
 467      /**
 468       * Returns the best size for a photo
 469       *
 470       * @param string $photoid the photo identifier
 471       * @return array of information provided by the API
 472       */
 473      protected function get_best_size($photoid) {
 474          if (!isset(self::$sizes[$photoid])) {
 475              // Sizes are returned from smallest to greatest.
 476              self::$sizes[$photoid] = $this->flickr->photos_getSizes($photoid);
 477          }
 478          $sizes = self::$sizes[$photoid];
 479          $bestsize = array();
 480          if (is_array($sizes)) {
 481              while ($bestsize = array_pop($sizes)) {
 482                  // Make sure the source is set. Exit the loop if found.
 483                  if (isset($bestsize['source'])) {
 484                      break;
 485                  }
 486              }
 487          }
 488          return $bestsize;
 489      }
 490  
 491      public function get_link($photoid) {
 492          return $this->build_photo_url($photoid);
 493      }
 494  
 495      /**
 496       *
 497       * @global object $CFG
 498       * @param string $photoid
 499       * @param string $file
 500       * @return string
 501       */
 502      public function get_file($photoid, $file = '') {
 503          global $CFG;
 504  
 505          $info = $this->flickr->photos_getInfo($photoid);
 506  
 507          // If we can read the original secret, it means that we have access to the original picture.
 508          if (isset($info['originalsecret'])) {
 509              $source = $this->flickr->buildPhotoURL($info, 'original');
 510          } else {
 511              $source = $this->build_photo_url($photoid);
 512          }
 513          // Make sure the source image exists.
 514          if (!@getimagesize($source)) {
 515              throw new moodle_exception('cannotdownload', 'repository');
 516          }
 517  
 518          if ($info['owner']['realname']) {
 519              $author = $info['owner']['realname'];
 520          } else {
 521              $author = $info['owner']['username'];
 522          }
 523          $copyright = get_string('author', 'repository') . ': ' . $author;
 524  
 525          $result = parent::get_file($source, $file);
 526          $path = $result['path'];
 527  
 528          if (!empty($this->usewatermarks)) {
 529              $img = new moodle_image($path);
 530              $img->watermark($copyright, array(10,10), array('ttf'=>true, 'fontsize'=>12))->saveas($path);
 531          }
 532  
 533          return array('path'=>$path, 'author'=>$info['owner']['realname'], 'license'=>$this->license4moodle($info['license']));
 534      }
 535  
 536      /**
 537       * Add Instance settings input to Moodle form
 538       * @param object $mform
 539       */
 540      public static function instance_config_form($mform) {
 541          $mform->addElement('text', 'email_address', get_string('emailaddress', 'repository_flickr_public'));
 542          $mform->setType('email_address', PARAM_RAW_TRIMMED); // This is for sending to flickr. Not our job to validate it.
 543          $mform->addElement('checkbox', 'usewatermarks', get_string('watermark', 'repository_flickr_public'));
 544          $mform->setDefault('usewatermarks', 0);
 545      }
 546  
 547      /**
 548       * Names of the instance settings
 549       * @return array
 550       */
 551      public static function get_instance_option_names() {
 552          return array('email_address', 'usewatermarks');
 553      }
 554  
 555      /**
 556       * Add Plugin settings input to Moodle form
 557       * @param object $mform
 558       */
 559      public static function type_config_form($mform, $classname = 'repository') {
 560          $api_key = get_config('flickr_public', 'api_key');
 561          if (empty($api_key)) {
 562              $api_key = '';
 563          }
 564          $strrequired = get_string('required');
 565  
 566          $mform->addElement('text', 'api_key', get_string('apikey', 'repository_flickr_public'), array('value'=>$api_key,'size' => '40'));
 567          $mform->setType('api_key', PARAM_RAW_TRIMMED);
 568          $mform->addRule('api_key', $strrequired, 'required', null, 'client');
 569  
 570          $mform->addElement('static', null, '',  get_string('information','repository_flickr_public'));
 571      }
 572  
 573      /**
 574       * Names of the plugin settings
 575       * @return array
 576       */
 577      public static function get_type_option_names() {
 578          return array('api_key', 'pluginname');
 579      }
 580  
 581      /**
 582       * is run when moodle administrator add the plugin
 583       */
 584      public static function plugin_init() {
 585          //here we create a default instance for this type
 586  
 587          $id = repository::static_function('flickr_public','create', 'flickr_public', 0, context_system::instance(), array('name'=>'', 'email_address' => null, 'usewatermarks' => false), 0);
 588          if (empty($id)) {
 589              return false;
 590          } else {
 591              return true;
 592          }
 593      }
 594      public function supported_filetypes() {
 595          return array('web_image');
 596      }
 597      public function supported_returntypes() {
 598          return (FILE_INTERNAL | FILE_EXTERNAL);
 599      }
 600  
 601      /**
 602       * Return the source information
 603       *
 604       * @param string $photoid photo id
 605       * @return string|null
 606       */
 607      public function get_file_source_info($photoid) {
 608          return $this->build_photo_url($photoid);
 609      }
 610  
 611      /**
 612       * Is this repository accessing private data?
 613       *
 614       * @return bool
 615       */
 616      public function contains_private_data() {
 617          return false;
 618      }
 619  }