Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 39 and 401]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    portfolio
  20   * @subpackage flickr
  21   * @copyright  2008 Nicolas Connault
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  require_once($CFG->libdir.'/portfolio/plugin.php');
  28  require_once($CFG->libdir.'/filelib.php');
  29  require_once($CFG->libdir.'/flickrclient.php');
  30  
  31  class portfolio_plugin_flickr extends portfolio_plugin_push_base {
  32  
  33      /** @var flickr_client */
  34      private $flickr;
  35      private $raw_sets;
  36  
  37      public function supported_formats() {
  38          return array(PORTFOLIO_FORMAT_IMAGE);
  39      }
  40  
  41      public static function get_name() {
  42          return get_string('pluginname', 'portfolio_flickr');
  43      }
  44  
  45      public function prepare_package() {
  46  
  47      }
  48  
  49      public function send_package() {
  50          foreach ($this->exporter->get_tempfiles() as $file) {
  51              // @TODO get max size from flickr people_getUploadStatus
  52              $filesize = $file->get_filesize();
  53  
  54              if ($this->is_valid_image($file)) {
  55                  $photoid = $this->flickr->upload($file, [
  56                      'title' => $this->get_export_config('title'),
  57                      'description' => $this->get_export_config('description'),
  58                      'tags' => $this->get_export_config('tags'),
  59                      'is_public' => $this->get_export_config('is_public'),
  60                      'is_friend' => $this->get_export_config('is_friend'),
  61                      'is_family' => $this->get_export_config('is_family'),
  62                      'safety_level' => $this->get_export_config('safety_level'),
  63                      'content_type' => $this->get_export_config('content_type'),
  64                      'hidden' => $this->get_export_config('hidden'),
  65                  ]);
  66  
  67                  if ($photoid === false) {
  68                      $this->set_user_config([
  69                          'accesstoken' => null,
  70                          'accesstokensecret' => null,
  71                      ]);
  72                      throw new portfolio_plugin_exception('uploadfailed', 'portfolio_flickr', '', 'Authentication failed');
  73                  }
  74  
  75                  // Attach photo to a set if requested.
  76                  if ($this->get_export_config('set')) {
  77                      $result = $this->flickr->call('photosets.addPhoto', [
  78                          'photoset_id' => $this->get_export_config('set'),
  79                          'photo_id' => $photoid,
  80                      ], 'POST');
  81                  }
  82              }
  83          }
  84      }
  85  
  86      public static function allows_multiple_instances() {
  87          return false;
  88      }
  89  
  90      public function get_interactive_continue_url() {
  91          return 'https://www.flickr.com/photos/organize';
  92      }
  93  
  94      public function expected_time($callertime) {
  95          return $callertime;
  96      }
  97  
  98      public static function get_allowed_config() {
  99          return array('apikey', 'sharedsecret');
 100      }
 101  
 102      public static function has_admin_config() {
 103          return true;
 104      }
 105  
 106      public static function admin_config_form(&$mform) {
 107          global $CFG;
 108  
 109          $strrequired = get_string('required');
 110          $mform->addElement('text', 'apikey', get_string('apikey', 'portfolio_flickr'), array('size' => 30));
 111          $mform->addRule('apikey', $strrequired, 'required', null, 'client');
 112          $mform->setType('apikey', PARAM_RAW_TRIMMED);
 113          $mform->addElement('text', 'sharedsecret', get_string('sharedsecret', 'portfolio_flickr'));
 114          $mform->addRule('sharedsecret', $strrequired, 'required', null, 'client');
 115          $mform->setType('sharedsecret', PARAM_RAW_TRIMMED);
 116          $a = new stdClass();
 117          $a->applyurl = 'http://www.flickr.com/services/api/keys/apply/';
 118          $a->keysurl = 'http://www.flickr.com/services/api/keys/';
 119          $a->callbackurl = $CFG->wwwroot . '/portfolio/add.php?postcontrol=1&type=flickr';
 120          $mform->addElement('static', 'setupinfo', get_string('setupinfo', 'portfolio_flickr'),
 121              get_string('setupinfodetails', 'portfolio_flickr', $a));
 122      }
 123  
 124      public function has_export_config() {
 125          return true;
 126      }
 127  
 128      public function get_allowed_user_config() {
 129          return array('accesstoken', 'accesstokensecret');
 130      }
 131  
 132      public function steal_control($stage) {
 133          if ($stage != PORTFOLIO_STAGE_CONFIG) {
 134              return false;
 135          }
 136  
 137          $accesstoken = $this->get_user_config('accesstoken');
 138          $accesstokensecret = $this->get_user_config('accesstokensecret');
 139  
 140          $callbackurl = new moodle_url('/portfolio/add.php', ['postcontrol' => 1, 'type' => 'flickr']);
 141          $this->flickr = new flickr_client($this->get_config('apikey'), $this->get_config('sharedsecret'), $callbackurl);
 142  
 143          if (!empty($accesstoken) && !empty($accesstokensecret)) {
 144              // The user has authenticated us already.
 145              $this->flickr->set_access_token($accesstoken, $accesstokensecret);
 146              return false;
 147          }
 148  
 149          $reqtoken = $this->flickr->request_token();
 150          $this->flickr->set_request_token_secret(['caller' => 'portfolio_flickr'], $reqtoken['oauth_token_secret']);
 151  
 152          $authurl = new moodle_url($reqtoken['authorize_url'], ['perms' => 'write']);
 153  
 154          return $authurl->out(false);
 155      }
 156  
 157      public function post_control($stage, $params) {
 158          if ($stage != PORTFOLIO_STAGE_CONFIG) {
 159              return;
 160          }
 161  
 162          if (empty($params['oauth_token']) || empty($params['oauth_verifier'])) {
 163              throw new portfolio_plugin_exception('noauthtoken', 'portfolio_flickr');
 164          }
 165  
 166          $callbackurl = new moodle_url('/portfolio/add.php', ['postcontrol' => 1, 'type' => 'flickr']);
 167          $this->flickr = new flickr_client($this->get_config('apikey'), $this->get_config('sharedsecret'), $callbackurl);
 168  
 169          $secret = $this->flickr->get_request_token_secret(['caller' => 'portfolio_flickr']);
 170  
 171          // Exchange the request token for the access token.
 172          $accesstoken = $this->flickr->get_access_token($params['oauth_token'], $secret, $params['oauth_verifier']);
 173  
 174          // Store the access token and the access token secret as the user
 175          // config so that we can use it on behalf of the user in next exports.
 176          $this->set_user_config([
 177              'accesstoken' => $accesstoken['oauth_token'],
 178              'accesstokensecret' => $accesstoken['oauth_token_secret'],
 179          ]);
 180      }
 181  
 182      public function export_config_form(&$mform) {
 183          $mform->addElement('text', 'plugin_title', get_string('title', 'portfolio_flickr'));
 184          $mform->setType('plugin_title', PARAM_TEXT);
 185          $mform->addElement('textarea', 'plugin_description', get_string('description'));
 186          $mform->setType('plugin_description', PARAM_CLEANHTML);
 187          $mform->addElement('text', 'plugin_tags', get_string('tags'));
 188          $mform->setType('plugin_tags', PARAM_TAGLIST);
 189          $mform->addElement('checkbox', 'plugin_is_public', get_string('ispublic', 'portfolio_flickr'));
 190          $mform->addElement('checkbox', 'plugin_is_family', get_string('isfamily', 'portfolio_flickr'));
 191          $mform->addElement('checkbox', 'plugin_is_friend', get_string('isfriend', 'portfolio_flickr'));
 192  
 193          $mform->disabledIf('plugin_is_friend', 'plugin_is_public', 'checked');
 194          $mform->disabledIf('plugin_is_family', 'plugin_is_public', 'checked');
 195  
 196          $safety_levels = array(1 => $this->get_export_value_name('safety_level', 1),
 197                                 2 => $this->get_export_value_name('safety_level', 2),
 198                                 3 => $this->get_export_value_name('safety_level', 3));
 199  
 200          $content_types = array(1 => $this->get_export_value_name('content_type', 1),
 201                                 2 => $this->get_export_value_name('content_type', 2),
 202                                 3 => $this->get_export_value_name('content_type', 3));
 203  
 204          $hidden_values = array(1,2);
 205  
 206          $mform->addElement('select', 'plugin_safety_level', get_string('safetylevel', 'portfolio_flickr'), $safety_levels);
 207          $mform->addElement('select', 'plugin_content_type', get_string('contenttype', 'portfolio_flickr'), $content_types);
 208          $mform->addElement('advcheckbox', 'plugin_hidden', get_string('hidefrompublicsearches', 'portfolio_flickr'), get_string('yes'), null, $hidden_values);
 209  
 210          $mform->setDefaults(array('plugin_is_public' => true));
 211  
 212          $rawsets = $this->get_sets();
 213          if (!empty($rawsets)) {
 214              $sets = array('0' => '----');
 215              foreach ($rawsets as $key => $value) {
 216                  $sets[$key] = $value;
 217              }
 218              $mform->addElement('select', 'plugin_set', get_string('set', 'portfolio_flickr'), $sets);
 219          }
 220      }
 221  
 222      /**
 223       * Fetches a list of current user's photosets (albums) on flickr.
 224       *
 225       * @return array (int)id => (string)title
 226       */
 227      private function get_sets() {
 228  
 229          if (empty($this->raw_sets)) {
 230              $this->raw_sets = $this->flickr->call('photosets.getList');
 231          }
 232  
 233          if ($this->raw_sets === false) {
 234              // Authentication failed, drop the locally stored token to force re-authentication.
 235              $this->set_user_config([
 236                  'accesstoken' => null,
 237                  'accesstokensecret' => null,
 238              ]);
 239              return array();
 240          }
 241  
 242          $sets = array();
 243          foreach ($this->raw_sets->photosets->photoset as $set) {
 244              $sets[$set->id] = $set->title->_content;
 245          }
 246          return $sets;
 247      }
 248  
 249      public function get_allowed_export_config() {
 250          return array('set', 'title', 'description', 'tags', 'is_public', 'is_family', 'is_friend', 'safety_level', 'content_type', 'hidden');
 251      }
 252  
 253      public function get_export_summary() {
 254          return array(get_string('set', 'portfolio_flickr') => $this->get_export_value_name('set', $this->get_export_config('set')),
 255                       get_string('title', 'portfolio_flickr') => $this->get_export_config('title'),
 256                       get_string('description') => $this->get_export_config('description'),
 257                       get_string('tags') => $this->get_export_config('tags'),
 258                       get_string('ispublic', 'portfolio_flickr') => $this->get_export_value_name('is_public', $this->get_export_config('is_public')),
 259                       get_string('isfamily', 'portfolio_flickr') => $this->get_export_value_name('is_family', $this->get_export_config('is_family')),
 260                       get_string('isfriend', 'portfolio_flickr') => $this->get_export_value_name('is_friend', $this->get_export_config('is_friend')),
 261                       get_string('safetylevel', 'portfolio_flickr') => $this->get_export_value_name('safety_level', $this->get_export_config('safety_level')),
 262                       get_string('contenttype', 'portfolio_flickr') => $this->get_export_value_name('content_type', $this->get_export_config('content_type')),
 263                       get_string('hidefrompublicsearches', 'portfolio_flickr') => $this->get_export_value_name('hidden', $this->get_export_config('hidden')));
 264      }
 265  
 266      private function get_export_value_name($param, $value) {
 267          $params = array('set' => $this->get_sets(),
 268                          'is_public' => array(0 => get_string('no'), 1 => get_string('yes')),
 269                          'is_family' => array(0 => get_string('no'), 1 => get_string('yes')),
 270                          'is_friend' => array(0 => get_string('no'), 1 => get_string('yes')),
 271                          'safety_level' => array(1 => get_string('safe', 'portfolio_flickr'),
 272                                                  2 => get_string('moderate', 'portfolio_flickr'),
 273                                                  3 => get_string('restricted', 'portfolio_flickr')),
 274                          'content_type' => array(1 => get_string('photo', 'portfolio_flickr'),
 275                                                  2 => get_string('screenshot', 'portfolio_flickr'),
 276                                                  3 => get_string('other', 'portfolio_flickr')),
 277                          'hidden' => array(1 => get_string('no'), 2 => get_string('yes')));
 278  
 279          if (isset($params[$param][$value])) {
 280              return $params[$param][$value];
 281          } else {
 282              return '-';
 283          }
 284      }
 285  
 286      /**
 287       * For now, flickr doesn't support this because we can't dynamically construct callbackurl
 288       */
 289      public static function allows_multiple_exports() {
 290          return false;
 291      }
 292  
 293      /**
 294       * Verifies the file is a valid optimised image - gif, png and jpeg only.
 295       * Currently, Flickr only supports these file types.
 296       *
 297       * @param stored_file $file
 298       * @return bool true if the file is ok
 299       */
 300      private function is_valid_image(stored_file $file): bool {
 301          $mimetype = $file->get_mimetype();
 302          if (!file_mimetype_in_typegroup($mimetype, 'optimised_image')) {
 303              return false;
 304          }
 305          if (!$info = $file->get_imageinfo()) {
 306              return false;
 307          }
 308          if ($info['mimetype'] !== $mimetype) {
 309              return false;
 310          }
 311          return true;
 312      }
 313  }