|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00032 defined('MOODLE_INTERNAL') || die(); 00033 00034 // require some of the sublibraries first. 00035 // this is not an exhaustive list, the others are pulled in as they're needed 00036 // so we don't have to always include everything unnecessarily for performance 00037 00038 // very lightweight list of constants. always needed and no further dependencies 00039 require_once($CFG->libdir . '/portfolio/constants.php'); 00040 // a couple of exception deinitions. always needed and no further dependencies 00041 require_once($CFG->libdir . '/portfolio/exceptions.php'); // exception classes used by portfolio code 00042 // The base class for the caller classes. We always need this because we're either drawing a button, 00043 // in which case the button needs to know the calling class definition, which requires the base class, 00044 // or we're exporting, in which case we need the caller class anyway. 00045 require_once($CFG->libdir . '/portfolio/caller.php'); 00046 00047 // the other dependencies are included on demand: 00048 // libdir/portfolio/formats.php - the classes for the export formats 00049 // libdir/portfolio/forms.php - all portfolio form classes (requires formslib) 00050 // libdir/portfolio/plugin.php - the base class for the export plugins 00051 // libdir/portfolio/exporter.php - the exporter class 00052 00053 00081 class portfolio_add_button { 00082 00083 private $callbackclass; 00084 private $callbackargs; 00085 private $callbackfile; 00086 private $formats; 00087 private $instances; 00088 private $file; // for single-file exports 00089 private $intendedmimetype; // for writing specific types of files 00090 00103 public function __construct($options=null) { 00104 global $SESSION, $CFG; 00105 00106 if (empty($CFG->enableportfolios)) { 00107 debugging('Building portfolio add button while portfolios is disabled. This code can be optimised.', DEBUG_DEVELOPER); 00108 } 00109 00110 $this->instances = portfolio_instances(); 00111 if (empty($options)) { 00112 return true; 00113 } 00114 $constructoroptions = array('callbackclass', 'callbackargs', 'callbackfile', 'formats'); 00115 foreach ((array)$options as $key => $value) { 00116 if (!in_array($key, $constructoroptions)) { 00117 throw new portfolio_button_exception('invalidbuttonproperty', 'portfolio', $key); 00118 } 00119 $this->{$key} = $value; 00120 } 00121 } 00122 00123 /* 00124 * @param string $class name of the class containing the callback functions 00125 * activity modules should ALWAYS use their name_portfolio_caller 00126 * other locations must use something unique 00127 * @param mixed $argarray this can be an array or hash of arguments to pass 00128 * back to the callback functions (passed by reference) 00129 * these MUST be primatives to be added as hidden form fields. 00130 * and the values get cleaned to PARAM_ALPHAEXT or PARAM_NUMBER or PARAM_PATH 00131 * @param string $file this can be autodetected if it's in the same file as your caller, 00132 * but often, the caller is a script.php and the class in a lib.php 00133 * so you can pass it here if necessary. 00134 * this path should be relative (ie, not include) dirroot, eg '/mod/forum/lib.php' 00135 */ 00136 public function set_callback_options($class, array $argarray, $file=null) { 00137 global $CFG; 00138 if (empty($file)) { 00139 $backtrace = debug_backtrace(); 00140 if (!array_key_exists(0, $backtrace) || !array_key_exists('file', $backtrace[0]) || !is_readable($backtrace[0]['file'])) { 00141 throw new portfolio_button_exception('nocallbackfile', 'portfolio'); 00142 } 00143 00144 $file = substr($backtrace[0]['file'], strlen($CFG->dirroot)); 00145 } else if (!is_readable($CFG->dirroot . $file)) { 00146 throw new portfolio_button_exception('nocallbackfile', 'portfolio', '', $file); 00147 } 00148 $this->callbackfile = $file; 00149 require_once($CFG->libdir . '/portfolio/caller.php'); // require the base class first 00150 require_once($CFG->dirroot . $file); 00151 if (!class_exists($class)) { 00152 throw new portfolio_button_exception('nocallbackclass', 'portfolio', '', $class); 00153 } 00154 00155 // this will throw exceptions 00156 // but should not actually do anything other than verify callbackargs 00157 $test = new $class($argarray); 00158 unset($test); 00159 00160 $this->callbackclass = $class; 00161 $this->callbackargs = $argarray; 00162 } 00163 00164 /* 00165 * sets the available export formats for this content 00166 * this function will also poll the static function in the caller class 00167 * and make sure we're not overriding a format that has nothing to do with mimetypes 00168 * eg if you pass IMAGE here but the caller can export LEAP2A it will keep LEAP2A as well. 00169 * see portfolio_most_specific_formats for more information 00170 * 00171 * @param array $formats if the calling code knows better than the static method on the calling class (base_supported_formats) 00172 * eg, if it's going to be a single file, or if you know it's HTML, you can pass it here instead 00173 * this is almost always the case so you should always use this. 00174 * {@see portfolio_format_from_mimetype} for how to get the appropriate formats to pass here for uploaded files. 00175 * or just call set_format_by_file instead 00176 */ 00177 public function set_formats($formats=null) { 00178 if (is_string($formats)) { 00179 $formats = array($formats); 00180 } 00181 if (empty($formats)) { 00182 $formats = array(); 00183 } 00184 if (empty($this->callbackclass)) { 00185 throw new portfolio_button_exception('noclassbeforeformats', 'portfolio'); 00186 } 00187 $callerformats = call_user_func(array($this->callbackclass, 'base_supported_formats')); 00188 $this->formats = portfolio_most_specific_formats($formats, $callerformats); 00189 } 00190 00195 public function reset_formats() { 00196 $this->set_formats(); 00197 } 00198 00199 00209 public function set_format_by_file(stored_file $file, $extraformats=null) { 00210 $this->file = $file; 00211 $fileformat = portfolio_format_from_mimetype($file->get_mimetype()); 00212 if (is_string($extraformats)) { 00213 $extraformats = array($extraformats); 00214 } else if (!is_array($extraformats)) { 00215 $extraformats = array(); 00216 } 00217 $this->set_formats(array_merge(array($fileformat), $extraformats)); 00218 } 00219 00228 public function set_format_by_intended_file($extn, $extraformats=null) { 00229 $mimetype = mimeinfo('type', 'something. ' . $extn); 00230 $fileformat = portfolio_format_from_mimetype($mimetype); 00231 $this->intendedmimetype = $fileformat; 00232 if (is_string($extraformats)) { 00233 $extraformats = array($extraformats); 00234 } else if (!is_array($extraformats)) { 00235 $extraformats = array(); 00236 } 00237 $this->set_formats(array_merge(array($fileformat), $extraformats)); 00238 } 00239 00240 /* 00241 * echo the form/button/icon/text link to the page 00242 * 00243 * @param int $format format to display the button or form or icon or link. 00244 * See constants PORTFOLIO_ADD_XXX for more info. 00245 * optional, defaults to PORTFOLIO_ADD_FULL_FORM 00246 * @param str $addstr string to use for the button or icon alt text or link text. 00247 * this is whole string, not key. optional, defaults to 'Export to portfolio'; 00248 */ 00249 public function render($format=null, $addstr=null) { 00250 echo $this->to_html($format, $addstr); 00251 } 00252 00253 /* 00254 * returns the form/button/icon/text link as html 00255 * 00256 * @param int $format format to display the button or form or icon or link. 00257 * See constants PORTFOLIO_ADD_XXX for more info. 00258 * optional, defaults to PORTFOLIO_ADD_FULL_FORM 00259 * @param str $addstr string to use for the button or icon alt text or link text. 00260 * this is whole string, not key. optional, defaults to 'Add to portfolio'; 00261 */ 00262 public function to_html($format=null, $addstr=null) { 00263 global $CFG, $COURSE, $OUTPUT, $USER; 00264 if (!$this->is_renderable()) { 00265 return; 00266 } 00267 if (empty($this->callbackclass) || empty($this->callbackfile)) { 00268 throw new portfolio_button_exception('mustsetcallbackoptions', 'portfolio'); 00269 } 00270 if (empty($this->formats)) { 00271 // use the caller defaults 00272 $this->set_formats(); 00273 } 00274 $url = new moodle_url('/portfolio/add.php'); 00275 foreach ($this->callbackargs as $key => $value) { 00276 if (!empty($value) && !is_string($value) && !is_numeric($value)) { 00277 $a = new stdClass(); 00278 $a->key = $key; 00279 $a->value = print_r($value, true); 00280 debugging(get_string('nonprimative', 'portfolio', $a)); 00281 return; 00282 } 00283 $url->param('ca_' . $key, $value); 00284 } 00285 $url->param('sesskey', sesskey()); 00286 $url->param('callbackfile', $this->callbackfile); 00287 $url->param('callbackclass', $this->callbackclass); 00288 $url->param('course', (!empty($COURSE)) ? $COURSE->id : 0); 00289 $url->param('callerformats', implode(',', $this->formats)); 00290 $mimetype = null; 00291 if ($this->file instanceof stored_file) { 00292 $mimetype = $this->file->get_mimetype(); 00293 } else if ($this->intendedmimetype) { 00294 $mimetype = $this->intendedmimetype; 00295 } 00296 $selectoutput = ''; 00297 if (count($this->instances) == 1) { 00298 $tmp = array_values($this->instances); 00299 $instance = $tmp[0]; 00300 00301 $formats = portfolio_supported_formats_intersect($this->formats, $instance->supported_formats()); 00302 if (count($formats) == 0) { 00303 // bail. no common formats. 00304 //debugging(get_string('nocommonformats', 'portfolio', (object)array('location' => $this->callbackclass, 'formats' => implode(',', $this->formats)))); 00305 return; 00306 } 00307 if ($error = portfolio_instance_sanity_check($instance)) { 00308 // bail, plugin is misconfigured 00309 //debugging(get_string('instancemisconfigured', 'portfolio', get_string($error[$instance->get('id')], 'portfolio_' . $instance->get('plugin')))); 00310 return; 00311 } 00312 if (!$instance->allows_multiple_exports() && $already = portfolio_existing_exports($USER->id, $instance->get('plugin'))) { 00313 //debugging(get_string('singleinstancenomultiallowed', 'portfolio')); 00314 return; 00315 } 00316 if ($mimetype&& !$instance->file_mime_check($mimetype)) { 00317 // bail, we have a specific file or mimetype and this plugin doesn't support it 00318 //debugging(get_string('mimecheckfail', 'portfolio', (object)array('plugin' => $instance->get('plugin'), 'mimetype' => $mimetype))); 00319 return; 00320 } 00321 $url->param('instance', $instance->get('id')); 00322 } 00323 else { 00324 if (!$selectoutput = portfolio_instance_select($this->instances, $this->formats, $this->callbackclass, $mimetype, 'instance', true)) { 00325 return; 00326 } 00327 } 00328 // if we just want a url to redirect to, do it now 00329 if ($format == PORTFOLIO_ADD_FAKE_URL) { 00330 return $url->out(false); 00331 } 00332 00333 if (empty($addstr)) { 00334 $addstr = get_string('addtoportfolio', 'portfolio'); 00335 } 00336 if (empty($format)) { 00337 $format = PORTFOLIO_ADD_FULL_FORM; 00338 } 00339 00340 $formoutput = '<form method="post" action="' . $CFG->wwwroot . '/portfolio/add.php" id="portfolio-add-button">' . "\n"; 00341 $formoutput .= html_writer::input_hidden_params($url); 00342 $linkoutput = '<a class="portfolio-add-link" title="'.$addstr.'" href="' . $url->out(); 00343 00344 switch ($format) { 00345 case PORTFOLIO_ADD_FULL_FORM: 00346 $formoutput .= $selectoutput; 00347 $formoutput .= "\n" . '<input type="submit" value="' . $addstr .'" />'; 00348 $formoutput .= "\n" . '</form>'; 00349 break; 00350 case PORTFOLIO_ADD_ICON_FORM: 00351 $formoutput .= $selectoutput; 00352 $formoutput .= "\n" . '<input class="portfolio-add-icon" type="image" src="' . $OUTPUT->pix_url('t/portfolioadd') . '" alt=' . $addstr .'" />'; 00353 $formoutput .= "\n" . '</form>'; 00354 break; 00355 case PORTFOLIO_ADD_ICON_LINK: 00356 $linkoutput .= '"><img class="portfolio-add-icon iconsmall" src="' . $OUTPUT->pix_url('t/portfolioadd') . '" alt="' . $addstr .'" /></a>'; 00357 break; 00358 case PORTFOLIO_ADD_TEXT_LINK: 00359 $linkoutput .= '">' . $addstr .'</a>'; 00360 break; 00361 default: 00362 debugging(get_string('invalidaddformat', 'portfolio', $format)); 00363 } 00364 $output = (in_array($format, array(PORTFOLIO_ADD_FULL_FORM, PORTFOLIO_ADD_ICON_FORM)) ? $formoutput : $linkoutput); 00365 return $output; 00366 } 00367 00373 private function is_renderable() { 00374 global $CFG; 00375 if (empty($CFG->enableportfolios)) { 00376 return false; 00377 } 00378 if (defined('PORTFOLIO_INTERNAL')) { 00379 // something somewhere has detected a risk of this being called during inside the preparation 00380 // eg forum_print_attachments 00381 return false; 00382 } 00383 if (empty($this->instances) || count($this->instances) == 0) { 00384 return false; 00385 } 00386 return true; 00387 } 00388 00393 public function get_formats() { 00394 return $this->formats; 00395 } 00396 00401 public function get_callbackargs() { 00402 return $this->callbackargs; 00403 } 00404 00409 public function get_callbackfile() { 00410 return $this->callbackfile; 00411 } 00412 00417 public function get_callbackclass() { 00418 return $this->callbackclass; 00419 } 00420 } 00421 00435 function portfolio_instance_select($instances, $callerformats, $callbackclass, $mimetype=null, $selectname='instance', $return=false, $returnarray=false) { 00436 global $CFG, $USER; 00437 00438 if (empty($CFG->enableportfolios)) { 00439 return; 00440 } 00441 00442 $insane = portfolio_instance_sanity_check(); 00443 $pinsane = portfolio_plugin_sanity_check(); 00444 00445 $count = 0; 00446 $selectoutput = "\n" . '<select name="' . $selectname . '">' . "\n"; 00447 $existingexports = portfolio_existing_exports_by_plugin($USER->id); 00448 foreach ($instances as $instance) { 00449 $formats = portfolio_supported_formats_intersect($callerformats, $instance->supported_formats()); 00450 if (count($formats) == 0) { 00451 // bail. no common formats. 00452 continue; 00453 } 00454 if (array_key_exists($instance->get('id'), $insane)) { 00455 // bail, plugin is misconfigured 00456 //debugging(get_string('instanceismisconfigured', 'portfolio', get_string($insane[$instance->get('id')], 'portfolio_' . $instance->get('plugin')))); 00457 continue; 00458 } else if (array_key_exists($instance->get('plugin'), $pinsane)) { 00459 // bail, plugin is misconfigured 00460 //debugging(get_string('pluginismisconfigured', 'portfolio', get_string($pinsane[$instance->get('plugin')], 'portfolio_' . $instance->get('plugin')))); 00461 continue; 00462 } 00463 if (!$instance->allows_multiple_exports() && in_array($instance->get('plugin'), $existingexports)) { 00464 // bail, already exporting something with this plugin and it doesn't support multiple exports 00465 continue; 00466 } 00467 if ($mimetype && !$instance->file_mime_check($mimetype)) { 00468 //debugging(get_string('mimecheckfail', 'portfolio', (object)array('plugin' => $instance->get('plugin'), 'mimetype' => $mimetype()))); 00469 // bail, we have a specific file and this plugin doesn't support it 00470 continue; 00471 } 00472 $count++; 00473 $selectoutput .= "\n" . '<option value="' . $instance->get('id') . '">' . $instance->get('name') . '</option>' . "\n"; 00474 $options[$instance->get('id')] = $instance->get('name'); 00475 } 00476 if (empty($count)) { 00477 // bail. no common formats. 00478 //debugging(get_string('nocommonformats', 'portfolio', (object)array('location' => $callbackclass, 'formats' => implode(',', $callerformats)))); 00479 return; 00480 } 00481 $selectoutput .= "\n" . "</select>\n"; 00482 if (!empty($returnarray)) { 00483 return $options; 00484 } 00485 if (!empty($return)) { 00486 return $selectoutput; 00487 } 00488 echo $selectoutput; 00489 } 00490 00501 function portfolio_instances($visibleonly=true, $useronly=true) { 00502 00503 global $DB, $USER; 00504 00505 $values = array(); 00506 $sql = 'SELECT * FROM {portfolio_instance}'; 00507 00508 if ($visibleonly || $useronly) { 00509 $values[] = 1; 00510 $sql .= ' WHERE visible = ?'; 00511 } 00512 if ($useronly) { 00513 $sql .= ' AND id NOT IN ( 00514 SELECT instance FROM {portfolio_instance_user} 00515 WHERE userid = ? AND name = ? AND ' . $DB->sql_compare_text('value') . ' = ? 00516 )'; 00517 $values = array_merge($values, array($USER->id, 'visible', 0)); 00518 } 00519 $sql .= ' ORDER BY name'; 00520 00521 $instances = array(); 00522 foreach ($DB->get_records_sql($sql, $values) as $instance) { 00523 $instances[$instance->id] = portfolio_instance($instance->id, $instance); 00524 } 00525 return $instances; 00526 } 00527 00537 function portfolio_supported_formats() { 00538 return array( 00539 PORTFOLIO_FORMAT_FILE => 'portfolio_format_file', 00540 PORTFOLIO_FORMAT_IMAGE => 'portfolio_format_image', 00541 PORTFOLIO_FORMAT_RICHHTML => 'portfolio_format_richhtml', 00542 PORTFOLIO_FORMAT_PLAINHTML => 'portfolio_format_plainhtml', 00543 PORTFOLIO_FORMAT_TEXT => 'portfolio_format_text', 00544 PORTFOLIO_FORMAT_VIDEO => 'portfolio_format_video', 00545 PORTFOLIO_FORMAT_PDF => 'portfolio_format_pdf', 00546 PORTFOLIO_FORMAT_DOCUMENT => 'portfolio_format_document', 00547 PORTFOLIO_FORMAT_SPREADSHEET => 'portfolio_format_spreadsheet', 00548 PORTFOLIO_FORMAT_PRESENTATION => 'portfolio_format_presentation', 00549 /*PORTFOLIO_FORMAT_MBKP, */ // later 00550 PORTFOLIO_FORMAT_LEAP2A => 'portfolio_format_leap2a', 00551 PORTFOLIO_FORMAT_RICH => 'portfolio_format_rich', 00552 ); 00553 } 00554 00569 function portfolio_format_from_mimetype($mimetype) { 00570 global $CFG; 00571 static $alreadymatched; 00572 if (empty($alreadymatched)) { 00573 $alreadymatched = array(); 00574 } 00575 if (array_key_exists($mimetype, $alreadymatched)) { 00576 return $alreadymatched[$mimetype]; 00577 } 00578 $allformats = portfolio_supported_formats(); 00579 require_once($CFG->libdir . '/portfolio/formats.php'); 00580 foreach ($allformats as $format => $classname) { 00581 $supportedmimetypes = call_user_func(array($classname, 'mimetypes')); 00582 if (!is_array($supportedmimetypes)) { 00583 debugging("one of the portfolio format classes, $classname, said it supported something funny for mimetypes, should have been array..."); 00584 debugging(print_r($supportedmimetypes, true)); 00585 continue; 00586 } 00587 if (in_array($mimetype, $supportedmimetypes)) { 00588 $alreadymatched[$mimetype] = $format; 00589 return $format; 00590 } 00591 } 00592 return PORTFOLIO_FORMAT_FILE; // base case for files... 00593 } 00594 00605 function portfolio_supported_formats_intersect($callerformats, $pluginformats) { 00606 global $CFG; 00607 $allformats = portfolio_supported_formats(); 00608 $intersection = array(); 00609 foreach ($callerformats as $cf) { 00610 if (!array_key_exists($cf, $allformats)) { 00611 if (!portfolio_format_is_abstract($cf)) { 00612 debugging(get_string('invalidformat', 'portfolio', $cf)); 00613 } 00614 continue; 00615 } 00616 require_once($CFG->libdir . '/portfolio/formats.php'); 00617 $cfobj = new $allformats[$cf](); 00618 foreach ($pluginformats as $p => $pf) { 00619 if (!array_key_exists($pf, $allformats)) { 00620 if (!portfolio_format_is_abstract($pf)) { 00621 debugging(get_string('invalidformat', 'portfolio', $pf)); 00622 } 00623 unset($pluginformats[$p]); // to avoid the same warning over and over 00624 continue; 00625 } 00626 if ($cfobj instanceof $allformats[$pf]) { 00627 $intersection[] = $cf; 00628 } 00629 } 00630 } 00631 return $intersection; 00632 } 00633 00641 function portfolio_format_is_abstract($format) { 00642 if (class_exists($format)) { 00643 $class = $format; 00644 } else if (class_exists('portfolio_format_' . $format)) { 00645 $class = 'portfolio_format_' . $format; 00646 } else { 00647 $allformats = portfolio_supported_formats(); 00648 if (array_key_exists($format, $allformats)) { 00649 $class = $allformats[$format]; 00650 } 00651 } 00652 if (empty($class)) { 00653 return true; // it may as well be, we can't instantiate it :) 00654 } 00655 $rc = new ReflectionClass($class); 00656 return $rc->isAbstract(); 00657 } 00658 00671 function portfolio_most_specific_formats($specificformats, $generalformats) { 00672 global $CFG; 00673 $allformats = portfolio_supported_formats(); 00674 if (empty($specificformats)) { 00675 return $generalformats; 00676 } else if (empty($generalformats)) { 00677 return $specificformats; 00678 } 00679 $removedformats = array(); 00680 foreach ($specificformats as $k => $f) { 00681 // look for something less specific and remove it, ie outside of the inheritance tree of the current formats. 00682 if (!array_key_exists($f, $allformats)) { 00683 if (!portfolio_format_is_abstract($f)) { 00684 throw new portfolio_button_exception('invalidformat', 'portfolio', $f); 00685 } 00686 } 00687 if (in_array($f, $removedformats)) { 00688 // already been removed from the general list 00689 //debugging("skipping $f because it was already removed"); 00690 unset($specificformats[$k]); 00691 } 00692 require_once($CFG->libdir . '/portfolio/formats.php'); 00693 $fobj = new $allformats[$f]; 00694 foreach ($generalformats as $key => $cf) { 00695 if (in_array($cf, $removedformats)) { 00696 //debugging("skipping $cf because it was already removed"); 00697 continue; 00698 } 00699 $cfclass = $allformats[$cf]; 00700 $cfobj = new $allformats[$cf]; 00701 if ($fobj instanceof $cfclass && $cfclass != get_class($fobj)) { 00702 //debugging("unsetting $key $cf because it's not specific enough ($f is better)"); 00703 unset($generalformats[$key]); 00704 $removedformats[] = $cf; 00705 continue; 00706 } 00707 // check for conflicts 00708 if ($fobj->conflicts($cf)) { 00709 //debugging("unsetting $key $cf because it conflicts with $f"); 00710 unset($generalformats[$key]); 00711 $removedformats[] = $cf; 00712 continue; 00713 } 00714 if ($cfobj->conflicts($f)) { 00715 //debugging("unsetting $key $cf because it reverse-conflicts with $f"); 00716 $removedformats[] = $cf; 00717 unset($generalformats[$key]); 00718 continue; 00719 } 00720 } 00721 //debugging('inside loop'); 00722 //print_object($generalformats); 00723 } 00724 00725 //debugging('final formats'); 00726 $finalformats = array_unique(array_merge(array_values($specificformats), array_values($generalformats))); 00727 //print_object($finalformats); 00728 return $finalformats; 00729 } 00730 00738 function portfolio_format_object($name) { 00739 global $CFG; 00740 require_once($CFG->libdir . '/portfolio/formats.php'); 00741 $formats = portfolio_supported_formats(); 00742 return new $formats[$name]; 00743 } 00744 00755 function portfolio_instance($instanceid, $record=null) { 00756 global $DB, $CFG; 00757 00758 if ($record) { 00759 $instance = $record; 00760 } else { 00761 if (!$instance = $DB->get_record('portfolio_instance', array('id' => $instanceid))) { 00762 throw new portfolio_exception('invalidinstance', 'portfolio'); 00763 } 00764 } 00765 require_once($CFG->libdir . '/portfolio/plugin.php'); 00766 require_once($CFG->dirroot . '/portfolio/'. $instance->plugin . '/lib.php'); 00767 $classname = 'portfolio_plugin_' . $instance->plugin; 00768 return new $classname($instanceid, $instance); 00769 } 00770 00781 function portfolio_static_function($plugin, $function) { 00782 global $CFG; 00783 00784 $pname = null; 00785 if (is_object($plugin) || is_array($plugin)) { 00786 $plugin = (object)$plugin; 00787 $pname = $plugin->name; 00788 } else { 00789 $pname = $plugin; 00790 } 00791 00792 $args = func_get_args(); 00793 if (count($args) <= 2) { 00794 $args = array(); 00795 } 00796 else { 00797 array_shift($args); 00798 array_shift($args); 00799 } 00800 00801 require_once($CFG->libdir . '/portfolio/plugin.php'); 00802 require_once($CFG->dirroot . '/portfolio/' . $plugin . '/lib.php'); 00803 return call_user_func_array(array('portfolio_plugin_' . $plugin, $function), $args); 00804 } 00805 00814 function portfolio_plugin_sanity_check($plugins=null) { 00815 global $DB; 00816 if (is_string($plugins)) { 00817 $plugins = array($plugins); 00818 } else if (empty($plugins)) { 00819 $plugins = get_plugin_list('portfolio'); 00820 $plugins = array_keys($plugins); 00821 } 00822 00823 $insane = array(); 00824 foreach ($plugins as $plugin) { 00825 if ($result = portfolio_static_function($plugin, 'plugin_sanity_check')) { 00826 $insane[$plugin] = $result; 00827 } 00828 } 00829 if (empty($insane)) { 00830 return array(); 00831 } 00832 list($where, $params) = $DB->get_in_or_equal(array_keys($insane)); 00833 $where = ' plugin ' . $where; 00834 $DB->set_field_select('portfolio_instance', 'visible', 0, $where, $params); 00835 return $insane; 00836 } 00837 00846 function portfolio_instance_sanity_check($instances=null) { 00847 global $DB; 00848 if (empty($instances)) { 00849 $instances = portfolio_instances(false); 00850 } else if (!is_array($instances)) { 00851 $instances = array($instances); 00852 } 00853 00854 $insane = array(); 00855 foreach ($instances as $instance) { 00856 if (is_object($instance) && !($instance instanceof portfolio_plugin_base)) { 00857 $instance = portfolio_instance($instance->id, $instance); 00858 } else if (is_numeric($instance)) { 00859 $instance = portfolio_instance($instance); 00860 } 00861 if (!($instance instanceof portfolio_plugin_base)) { 00862 debugging('something weird passed to portfolio_instance_sanity_check, not subclass or id'); 00863 continue; 00864 } 00865 if ($result = $instance->instance_sanity_check()) { 00866 $insane[$instance->get('id')] = $result; 00867 } 00868 } 00869 if (empty($insane)) { 00870 return array(); 00871 } 00872 list ($where, $params) = $DB->get_in_or_equal(array_keys($insane)); 00873 $where = ' id ' . $where; 00874 $DB->set_field_select('portfolio_instance', 'visible', 0, $where, $params); 00875 portfolio_insane_notify_admins($insane, true); 00876 return $insane; 00877 } 00878 00886 function portfolio_report_insane($insane, $instances=false, $return=false) { 00887 global $OUTPUT; 00888 if (empty($insane)) { 00889 return; 00890 } 00891 00892 static $pluginstr; 00893 if (empty($pluginstr)) { 00894 $pluginstr = get_string('plugin', 'portfolio'); 00895 } 00896 if ($instances) { 00897 $headerstr = get_string('someinstancesdisabled', 'portfolio'); 00898 } else { 00899 $headerstr = get_string('somepluginsdisabled', 'portfolio'); 00900 } 00901 00902 $output = $OUTPUT->notification($headerstr, 'notifyproblem'); 00903 $table = new html_table(); 00904 $table->head = array($pluginstr, ''); 00905 $table->data = array(); 00906 foreach ($insane as $plugin => $reason) { 00907 if ($instances) { 00908 $instance = $instances[$plugin]; 00909 $plugin = $instance->get('plugin'); 00910 $name = $instance->get('name'); 00911 } else { 00912 $name = $plugin; 00913 } 00914 $table->data[] = array($name, get_string($reason, 'portfolio_' . $plugin)); 00915 } 00916 $output .= html_writer::table($table); 00917 $output .= '<br /><br /><br />'; 00918 00919 if ($return) { 00920 return $output; 00921 } 00922 echo $output; 00923 } 00924 00925 00929 function portfolio_handle_event($eventdata) { 00930 global $CFG; 00931 00932 require_once($CFG->libdir . '/portfolio/exporter.php'); 00933 $exporter = portfolio_exporter::rewaken_object($eventdata); 00934 $exporter->process_stage_package(); 00935 $exporter->process_stage_send(); 00936 $exporter->save(); 00937 $exporter->process_stage_cleanup(); 00938 return true; 00939 } 00940 00947 function portfolio_cron() { 00948 global $DB, $CFG; 00949 00950 require_once($CFG->libdir . '/portfolio/exporter.php'); 00951 if ($expired = $DB->get_records_select('portfolio_tempdata', 'expirytime < ?', array(time()), '', 'id')) { 00952 foreach ($expired as $d) { 00953 try { 00954 $e = portfolio_exporter::rewaken_object($d->id); 00955 $e->process_stage_cleanup(true); 00956 } catch (Exception $e) { 00957 mtrace('Exception thrown in portfolio cron while cleaning up ' . $d->id . ': ' . $e->getMessage()); 00958 } 00959 } 00960 } 00961 } 00962 00975 function portfolio_export_rethrow_exception($exporter, $exception) { 00976 throw new portfolio_export_exception($exporter, $exception->errorcode, $exception->module, $exception->link, $exception->a); 00977 } 00978 00987 function portfolio_expected_time_file($totest) { 00988 global $CFG; 00989 if ($totest instanceof stored_file) { 00990 $totest = array($totest); 00991 } 00992 $size = 0; 00993 foreach ($totest as $file) { 00994 if (!($file instanceof stored_file)) { 00995 debugging('something weird passed to portfolio_expected_time_file - not stored_file object'); 00996 debugging(print_r($file, true)); 00997 continue; 00998 } 00999 $size += $file->get_filesize(); 01000 } 01001 01002 $fileinfo = portfolio_filesize_info(); 01003 01004 $moderate = $high = 0; // avoid warnings 01005 01006 foreach (array('moderate', 'high') as $setting) { 01007 $settingname = 'portfolio_' . $setting . '_filesize_threshold'; 01008 if (empty($CFG->{$settingname}) || !array_key_exists($CFG->{$settingname}, $fileinfo['options'])) { 01009 debugging("weird or unset admin value for $settingname, using default instead"); 01010 $$setting = $fileinfo[$setting]; 01011 } else { 01012 $$setting = $CFG->{$settingname}; 01013 } 01014 } 01015 01016 if ($size < $moderate) { 01017 return PORTFOLIO_TIME_LOW; 01018 } else if ($size < $high) { 01019 return PORTFOLIO_TIME_MODERATE; 01020 } 01021 return PORTFOLIO_TIME_HIGH; 01022 } 01023 01024 01029 function portfolio_filesize_info() { 01030 $filesizes = array(); 01031 $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152, 5242880, 10485760, 20971520, 52428800); 01032 foreach ($sizelist as $size) { 01033 $filesizes[$size] = display_size($size); 01034 } 01035 return array( 01036 'options' => $filesizes, 01037 'moderate' => 1048576, 01038 'high' => 5242880, 01039 ); 01040 } 01041 01050 function portfolio_expected_time_db($recordcount) { 01051 global $CFG; 01052 01053 if (empty($CFG->portfolio_moderate_dbsize_threshold)) { 01054 set_config('portfolio_moderate_dbsize_threshold', 10); 01055 } 01056 if (empty($CFG->portfolio_high_dbsize_threshold)) { 01057 set_config('portfolio_high_dbsize_threshold', 50); 01058 } 01059 if ($recordcount < $CFG->portfolio_moderate_dbsize_threshold) { 01060 return PORTFOLIO_TIME_LOW; 01061 } else if ($recordcount < $CFG->portfolio_high_dbsize_threshold) { 01062 return PORTFOLIO_TIME_MODERATE; 01063 } 01064 return PORTFOLIO_TIME_HIGH; 01065 } 01066 01070 function portfolio_insane_notify_admins($insane, $instances=false) { 01071 01072 global $CFG; 01073 01074 if (defined('ADMIN_EDITING_PORTFOLIO')) { 01075 return true; 01076 } 01077 01078 $admins = get_admins(); 01079 01080 if (empty($admins)) { 01081 return; 01082 } 01083 if ($instances) { 01084 $instances = portfolio_instances(false, false); 01085 } 01086 01087 $site = get_site(); 01088 01089 $a = new StdClass; 01090 $a->sitename = format_string($site->fullname, true, array('context' => get_context_instance(CONTEXT_COURSE, SITEID))); 01091 $a->fixurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=manageportfolios"; 01092 $a->htmllist = portfolio_report_insane($insane, $instances, true); 01093 $a->textlist = ''; 01094 01095 foreach ($insane as $k => $reason) { 01096 if ($instances) { 01097 $a->textlist = $instances[$k]->get('name') . ': ' . $reason . "\n"; 01098 } else { 01099 $a->textlist = $k . ': ' . $reason . "\n"; 01100 } 01101 } 01102 01103 $subject = get_string('insanesubject', 'portfolio'); 01104 $plainbody = get_string('insanebody', 'portfolio', $a); 01105 $htmlbody = get_string('insanebodyhtml', 'portfolio', $a); 01106 $smallbody = get_string('insanebodysmall', 'portfolio', $a); 01107 01108 foreach ($admins as $admin) { 01109 $eventdata = new stdClass(); 01110 $eventdata->modulename = 'portfolio'; 01111 $eventdata->component = 'portfolio'; 01112 $eventdata->name = 'notices'; 01113 $eventdata->userfrom = $admin; 01114 $eventdata->userto = $admin; 01115 $eventdata->subject = $subject; 01116 $eventdata->fullmessage = $plainbody; 01117 $eventdata->fullmessageformat = FORMAT_PLAIN; 01118 $eventdata->fullmessagehtml = $htmlbody; 01119 $eventdata->smallmessage = $smallbody; 01120 message_send($eventdata); 01121 } 01122 } 01123 01124 function portfolio_export_pagesetup($PAGE, $caller) { 01125 // set up the context so that build_navigation works nice 01126 $caller->set_context($PAGE); 01127 01128 list($extranav, $cm) = $caller->get_navigation(); 01129 01130 // and now we know the course for sure and maybe the cm, call require_login with it 01131 require_login($PAGE->course, false, $cm); 01132 01133 foreach ($extranav as $navitem) { 01134 $PAGE->navbar->add($navitem['name']); 01135 } 01136 $PAGE->navbar->add(get_string('exporting', 'portfolio')); 01137 } 01138 01139 function portfolio_export_type_to_id($type, $userid) { 01140 global $DB; 01141 $sql = 'SELECT t.id FROM {portfolio_tempdata} t JOIN {portfolio_instance} i ON t.instance = i.id WHERE t.userid = ? AND i.plugin = ?'; 01142 return $DB->get_field_sql($sql, array($userid, $type)); 01143 } 01144 01156 function portfolio_existing_exports($userid, $type=null) { 01157 global $DB; 01158 $sql = 'SELECT t.*,t.instance,i.plugin,i.name FROM {portfolio_tempdata} t JOIN {portfolio_instance} i ON t.instance = i.id WHERE t.userid = ? '; 01159 $values = array($userid); 01160 if ($type) { 01161 $sql .= ' AND i.plugin = ?'; 01162 $values[] = $type; 01163 } 01164 return $DB->get_records_sql($sql, $values); 01165 } 01166 01172 function portfolio_existing_exports_by_plugin($userid) { 01173 global $DB; 01174 $sql = 'SELECT t.id,i.plugin FROM {portfolio_tempdata} t JOIN {portfolio_instance} i ON t.instance = i.id WHERE t.userid = ? '; 01175 $values = array($userid); 01176 return $DB->get_records_sql_menu($sql, $values); 01177 } 01178 01186 function portfolio_format_text_options() { 01187 01188 $options = new stdClass(); 01189 $options->para = false; 01190 $options->newlines = true; 01191 $options->filter = false; 01192 $options->noclean = true; 01193 $options->overflowdiv = false; 01194 01195 return $options; 01196 } 01197 01202 function portfolio_rewrite_pluginfile_url_callback($contextid, $component, $filearea, $itemid, $format, $options, $matches) { 01203 $matches = $matches[0]; // no internal matching 01204 $dom = new DomDocument(); 01205 if (!$dom->loadXML($matches)) { 01206 return $matches; 01207 } 01208 $attributes = array(); 01209 foreach ($dom->documentElement->attributes as $attr => $node) { 01210 $attributes[$attr] = $node->value; 01211 } 01212 // now figure out the file 01213 $fs = get_file_storage(); 01214 $key = 'href'; 01215 if (!array_key_exists('href', $attributes) && array_key_exists('src', $attributes)) { 01216 $key = 'src'; 01217 } 01218 if (!array_key_exists($key, $attributes)) { 01219 debugging('Couldn\'t find an attribute to use that contains @@PLUGINFILE@@ in portfolio_rewrite_pluginfile'); 01220 return $matches; 01221 } 01222 $filename = substr($attributes[$key], strpos($attributes[$key], '@@PLUGINFILE@@') + strlen('@@PLUGINFILE@@')); 01223 $filepath = '/'; 01224 if (strpos($filename, '/') !== 0) { 01225 $bits = explode('/', $filename); 01226 $filename = array_pop($bits); 01227 $filepath = implode('/', $bits); 01228 } 01229 if (!$file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 01230 debugging("Couldn't find a file from the embedded path info context $contextid component $component filearea $filearea itemid $itemid filepath $filepath name $filename"); 01231 return $matches; 01232 } 01233 if (empty($options)) { 01234 $options = array(); 01235 } 01236 $options['attributes'] = $attributes; 01237 return $format->file_output($file, $options); 01238 } 01239 01240 01256 function portfolio_rewrite_pluginfile_urls($text, $contextid, $component, $filearea, $itemid, $format, $options=null) { 01257 $pattern = '/(<[^<]*?="@@PLUGINFILE@@\/[^>]*?(?:\/>|>.*?<\/[^>]*?>))/'; 01258 $callback = partial('portfolio_rewrite_pluginfile_url_callback', $contextid, $component, $filearea, $itemid, $format, $options); 01259 return preg_replace_callback($pattern, $callback, $text); 01260 } 01261 // this function has to go last, because the regexp screws up syntax highlighting in some editors 01262