Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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