public static function fromString(string $marc): array
{
$leader = substr($marc, 0, 24);
$fields = [];
$dataStart = 0 + (int)substr($marc, 12, 5);
$dirLen = $dataStart - self::LEADER_LEN - 1;
$offset = 0;
while ($offset < $dirLen) {
$tag = substr($marc, self::LEADER_LEN + $offset, 3);
$len = (int)substr($marc, self::LEADER_LEN + $offset + 3, 4);
$dataOffset
= (int)substr($marc, self::LEADER_LEN + $offset + 7, 5);
$tagData = substr($marc, $dataStart + $dataOffset, $len);
if (substr($tagData, -1, 1) == self::END_OF_FIELD) {
$tagData = substr($tagData, 0, -1);
} else {
throw new \Exception(
"Invalid MARC record (end of field not found): $marc"
);
}
if (ctype_digit($tag) && $tag < 10) {
$fields[$tag][] = $tagData;
} else {
$newField = [
'i1' => $tagData[0] ?? ' ',
'i2' => $tagData[1] ?? ' '
];
$subfields = explode(
self::SUBFIELD_INDICATOR,
substr($tagData, 3)
);
foreach ($subfields as $subfield) {
if ('' === $subfield) {
continue;
}
$newField['s'][] = [
public function __construct($data)
{
$this->setData($data);
}
/**
* Set MARC record data
*
* @param string $data MARC record in MARCXML or ISO2709 format
*
* @throws Exception
* @return void
*/
public function setData(string $data): void
{
$leader = null;
$valid = false;
foreach ($this->serializations as $serialization) {
if ($serialization::canParse($data)) {
[$leader, $this->fields] = $serialization::fromString($data);
$valid = true;
break;
}
}
if (!$valid) {
throw new \Exception('MARC record format not recognized');
}
// Make sure leader is 24 characters
$this->leader = $leader ? str_pad(substr($leader, 0, 24), 24) : '';
}
/**
* Serialize the record
*
* @param string $format Format to return (e.g. 'ISO2709' or 'MARCXML')
*
* @return string
*/
public function toFormat(string $format): string
{
* i2: '1'
* s: [
* ['a' => 'Title'],
* ['k' => 'Form'],
* ['k' => 'Another'],
* ['p' => 'Part'],
* ]
*
* @var array
*/
protected $fields;
/**
* Constructor
*
* @param string $data MARC record in MARCXML or ISO2709 format
*/
public function __construct($data)
{
$this->setData($data);
}
/**
* Set MARC record data
*
* @param string $data MARC record in MARCXML or ISO2709 format
*
* @throws Exception
* @return void
*/
public function setData(string $data): void
{
$leader = null;
$valid = false;
foreach ($this->serializations as $serialization) {
if ($serialization::canParse($data)) {
[$leader, $this->fields] = $serialization::fromString($data);
$valid = true;
break;
}
$preferredMarcField = 'fullrecord';
foreach ($preferredMarcFieldArray as $testField) {
if (array_key_exists($testField, $this->fields)) {
$preferredMarcField = $testField;
break;
}
}
return trim($this->fields[$preferredMarcField]);
}
/**
* Get access to the MarcReader object.
*
* @return \VuFind\Marc\MarcReader
*/
public function getMarcReader()
{
if (null === $this->lazyMarcReader) {
$this->lazyMarcReader = new $this->marcReaderClass(
$this->getRawMarcData()
);
}
return $this->lazyMarcReader;
}
/**
* Get access to the raw File_MARC object.
*
* @return \File_MARC_Record
* @deprecated Use getMarcReader()
*/
public function getMarcRecord()
{
if (null === $this->lazyMarcRecord) {
$marc = $this->getRawMarcData();
// check if we are dealing with MARCXML
if (substr($marc, 0, 1) == '<') {
$marc = new \File_MARCXML($marc, \File_MARCXML::SOURCE_STRING);
{
$fields024 = $this->getMarcReader()->getFields('024');
foreach ($fields024 as $field) {
if ($field['i1'] == 2
&& $subfield = $this->getSubfield($field, 'a')
) {
return $subfield;
}
}
return false;
}
/**
* Return first national bibliography number found, or false if not found
*
* @return mixed
*/
public function getCleanNBN()
{
$field = $this->getMarcReader()->getField('015');
if ($field && $nbn = $this->getSubfield($field, 'a')) {
$result = compact('nbn');
if ($source = $this->getSubfield($field, '7')) {
$result['source'] = $source;
}
return $result;
}
return false;
}
/**
* Get the full titles of the record in alternative scripts.
*
* @return array
*/
public function getTitlesAltScript(): array
{
return $this->getMarcReader()
->getLinkedFieldsSubfields('880', '245', ['a', 'b']);
}
'author' => mb_substr($this->getPrimaryAuthor(), 0, 300, 'utf-8'),
'callnumber' => $this->getCallNumber(),
'size' => $size,
'title' => mb_substr($this->getTitle(), 0, 300, 'utf-8'),
'recordid' => $this->getUniqueID(),
'source' => $this->getSourceIdentifier(),
];
if ($isbn = $this->getCleanISBN()) {
$arr['isbn'] = $isbn;
}
if ($issn = $this->getCleanISSN()) {
$arr['issn'] = $issn;
}
if ($oclc = $this->getCleanOCLCNum()) {
$arr['oclc'] = $oclc;
}
if ($upc = $this->getCleanUPC()) {
$arr['upc'] = $upc;
}
if ($nbn = $this->getCleanNBN()) {
$arr['nbn'] = $nbn['nbn'];
}
if ($ismn = $this->getCleanISMN()) {
$arr['ismn'] = $ismn;
}
// If an ILS driver has injected extra details, check for IDs in there
// to fill gaps:
if ($ilsDetails = $this->getExtraDetail('ils_details')) {
foreach (['isbn', 'issn', 'oclc', 'upc', 'nbn', 'ismn'] as $key) {
if (!isset($arr[$key]) && isset($ilsDetails[$key])) {
$arr[$key] = $ilsDetails[$key];
}
}
}
return $arr;
}
/**
* Get the full title of the record.
public function getExtraDetail($key)
{
return $this->extraDetails[$key] ?? null;
}
/**
* Try to call the requested method and return null if it is unavailable; this is
* useful for checking for the existence of get methods for particular types of
* data without causing fatal errors.
*
* @param string $method Name of method to call.
* @param array $params Array of parameters to pass to method.
* @param mixed $default A default value to return if the method is not
* callable
*
* @return mixed
*/
public function tryMethod($method, $params = [], $default = null)
{
return is_callable([$this, $method]) ? $this->$method(...$params) : $default;
}
}
* @param string $size Size of thumbnail (small, medium or large;
* small is default).
* @param bool $resolveDynamic Should we resolve dynamic cover data into
* a URL (true) or simply return false (false)?
* @param bool $testLoadImage If true the function will try to load the
* cover image in advance and returns false in case no image could be loaded
* @param bool $ajax True if the function is called from ajax
* handler
*
* @return false|array|null
*/
public function getMetadata(
RecordDriver $driver,
$size = 'small',
$resolveDynamic = true,
$testLoadImage = false,
$ajax = false
) {
// Try to build thumbnail:
$thumb = $driver->tryMethod('getThumbnail', [$size]);
// No thumbnail? Return false:
if (empty($thumb)) {
return false;
}
// Array? It's parameters to send to the cover generator:
if (is_array($thumb)) {
if (!$resolveDynamic) {
return null;
}
$dynamicUrl = $this->dynamicUrl . '?' . http_build_query($thumb);
} else {
return ['url' => $thumb];
}
$settings = is_array($thumb) ? array_merge($thumb, ['size' => $size])
: ['size' => $size];
$handlers = $this->coverLoader->getHandlers();
$ids = $this->coverLoader->getIdentifiersForSettings($settings);
* that a subsequent AJAX check is needed).
*
* @param RecordDriver $driver Record driver
* @param string $size Size of thumbnail (small, medium or large;
* small is default).
* @param bool $resolveDynamic Should we resolve dynamic cover data into
* a URL (true) or simply return false (false)?
* @param bool $testLoadImage If true the function will try to load the
* cover image in advance and returns false in case no image could be loaded
*
* @return string|false|null
*/
public function getUrl(
RecordDriver $driver,
$size = 'small',
$resolveDynamic = true,
$testLoadImage = false
) {
$metadata = $this->getMetadata(
$driver,
$size,
$resolveDynamic,
$testLoadImage
);
// getMetadata could return null or false, that is the reason we are
// respecting the returned value - in case it is not empty array to be on
// safe side and not return bad type here
return $metadata['url'] ?? (!is_array($metadata) ? $metadata : false);
}
/**
* Generate a thumbnail metadata (return false if unsupported; return null to
* indicate that a subsequent AJAX check is needed).
*
* @param RecordDriver $driver Record driver
* @param string $size Size of thumbnail (small, medium or large;
* small is default).
* @param bool $resolveDynamic Should we resolve dynamic cover data into
* a URL (true) or simply return false (false)?
* @param bool $testLoadImage If true the function will try to load the
$urlHelper = $this->getView()->plugin('url');
return $urlHelper('qrcode-show') . '?' . http_build_query($qrcode);
}
/**
* Generate a thumbnail URL (return false if unsupported).
*
* @param string $size Size of thumbnail (small, medium or large -- small is
* default).
*
* @return string|bool
*/
public function getThumbnail($size = 'small')
{
// Find out whether or not AJAX covers are enabled; this will control
// whether dynamic URLs are resolved immediately or deferred until later
// (see third parameter of getUrl() below).
$ajaxcovers = $this->config->Content->ajaxcovers ?? false;
return $this->coverRouter
? $this->coverRouter->getUrl($this->driver, $size, !$ajaxcovers)
: false;
}
/**
* Get all URLs associated with the record. Returns an array of strings.
*
* @return array
*/
public function getUrlList()
{
// Use a filter to pick URLs from the output of getLinkDetails():
$filter = function ($i) {
return $i['url'];
};
return array_map($filter, $this->getLinkDetails());
}
/**
* Get all the links associated with this record. Returns an array of
* associative arrays each containing 'desc' and 'url' keys.
*
* @param string $context Context of code being generated
* @param string $default The default size of the cover
* @param string $link The link for the anchor
*
* @return array
*/
public function getCoverDetails($context, $default, $link = false)
{
$details = compact('link', 'context') + [
'driver' => $this->driver, 'cover' => false, 'size' => false,
'linkPreview' => $this->getPreviewCoverLinkSetting($context),
];
$preferredSize = $this->getCoverSize($context, $default);
if (empty($preferredSize)) { // covers disabled entirely
$details['html'] = '';
} else {
// Find best option if more than one size is defined (e.g. small:medium)
foreach (explode(':', $preferredSize) as $size) {
if ($details['cover'] = $this->getThumbnail($size)) {
$details['size'] = $size;
break;
}
}
if ($details['size'] === false) {
[$details['size']] = explode(':', $preferredSize);
}
$details['html'] = $this->renderTemplate('cover.phtml', $details);
}
return $details;
}
/**
* Get the configured thumbnail size for record lists
*
* @param string $context Context of code being generated
* @param string $default The default size of the cover
*
* @return string
*/
<?php
$coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $this->recordLinker()->getUrl($this->driver));
$cover = $coverDetails['html'];
$thumbnail = false;
$thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result');
if ($cover):
ob_start(); ?>
<div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
<?=$cover ?>
</div>
<?php $thumbnail = ob_get_contents(); ?>
<?php ob_end_clean(); ?>
<?php endif; ?>
<input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" />
<input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" />
<div class="media">
<?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
<?=$thumbnail ?>
<?php endif ?>
<div class="media-body">
<div class="result-body">
<div>
<a href="<?=$this->escapeHtmlAttr($this->recordLinker()->getUrl($this->driver))?>" class="title getFull" data-view="<?=isset($this->params) ? $this->params->getOptions()->getListViewOption() : 'list' ?>">
<?=$this->record($this->driver)->getTitleHtml()?>
</a>
<?php foreach ($this->driver->tryMethod('getTitlesAltScript', [], []) as $altTitle): ?>
<div class="title-alt">
<?=$this->escapeHtml($altTitle)?>
</div>
<?php endforeach; ?>
</div>
<div>
<?php if($this->driver->isCollection()): ?>
<?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?>
<?php else: ?>
<?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?>
<?=$this->transEsc('by')?>
<?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?>
<a href="<?=$this->record($this->driver)->getLink('author', $this->highlight($summAuthor, null, true, false))?>"><?=$this->highlight(rtrim($summAuthor, ','))?></a><?=$i + 1 < $authorCount ? ',' : ''?>
$__vars = $this->vars()->getArrayCopy();
if (array_key_exists('this', $__vars)) {
unset($__vars['this']);
}
extract($__vars);
unset($__vars); // remove $__vars from local scope
$this->__content = '';
while ($this->__template = array_pop($this->__templates)) {
$this->__file = $this->resolver($this->__template);
if (! $this->__file) {
throw new Exception\RuntimeException(sprintf(
'%s: Unable to render template "%s"; resolver could not resolve to a file',
__METHOD__,
$this->__template
));
}
try {
ob_start();
$includeReturn = include $this->__file;
$this->__content = ob_get_clean();
} catch (\Throwable $ex) {
ob_end_clean();
throw $ex;
} catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced
ob_end_clean();
throw $ex;
}
if ($includeReturn === false && empty($this->__content)) {
throw new Exception\UnexpectedValueException(sprintf(
'%s: Unable to render template "%s"; file include failed',
__METHOD__,
$this->__file
));
}
}
$this->setVars(array_pop($this->__varsCache));
if ($this->__filterChain instanceof FilterChain) {
$className,
$context = [],
$throw = true
) {
// Set up the needed context in the view:
$view = $this->getView();
$contextHelper = $view->plugin('context');
$oldContext = $contextHelper($view)->apply($context);
// Find and render the template:
$classTemplate = $this->getCachedClassTemplate($template, $className);
if (!$classTemplate && $throw) {
throw new RuntimeException(
'Cannot find '
. $this->getTemplateWithClass($template, '[brief class name]')
. " for class $className or any of its parent classes"
);
}
$html = $classTemplate ? $view->render($classTemplate) : '';
// Restore the original context before returning the result:
$contextHelper($view)->restore($oldContext);
return $html;
}
/**
* Resolve the class template file unless already cached and return the file
* name.
*
* @param string $template Template path (with %s as class name placeholder)
* @param string $className Name of class to apply to template.
*
* @return string
*/
protected function getCachedClassTemplate($template, $className)
{
if (!isset($this->templateCache[$className][$template])) {
$this->templateCache[$className][$template]
= $this->resolveClassTemplate(
}
/**
* Render a template within a record driver folder.
*
* @param string $name Template name to render
* @param array $context Variables needed for rendering template; these will
* be temporarily added to the global view context, then reverted after the
* template is rendered (default = record driver only).
* @param bool $throw If true (default), an exception is thrown if the
* template is not found. Otherwise an empty string is returned.
*
* @return string
*/
public function renderTemplate($name, $context = null, $throw = true)
{
$template = 'RecordDriver/%s/' . $name;
$className = get_class($this->driver);
return $this->renderClassTemplate(
$template,
$className,
$context ?? ['driver' => $this->driver],
$throw
);
}
/**
* Store a record driver object and return this object so that the appropriate
* template can be rendered.
*
* @param \VuFind\RecordDriver\AbstractBase $driver Record driver object.
*
* @return Record
*/
public function __invoke($driver)
{
// Set up context helper:
$contextHelper = $this->getView()->plugin('context');
$this->contextHelper = $contextHelper($this->getView());
/**
* Render a toolbar for use on the record view.
*
* @return string
*/
public function getToolbar()
{
return $this->renderTemplate('toolbar.phtml');
}
/**
* Render a search result for the specified view mode.
*
* @param string $view View mode to use.
*
* @return string
*/
public function getSearchResult($view)
{
return $this->renderTemplate('result-' . $view . '.phtml');
}
/**
* Render an HTML checkbox control for the current record.
*
* @param string $idPrefix Prefix for checkbox HTML ids
* @param string $formAttr ID of form for [form] attribute
* @param int $number Result number (for label of checkbox)
*
* @return string
*/
public function getCheckbox($idPrefix = '', $formAttr = false, $number = null)
{
$id = $this->driver->getSourceIdentifier() . '|'
. $this->driver->getUniqueId();
$context
= ['id' => $id, 'number' => $number, 'prefix' => $idPrefix];
if ($formAttr) {
$context['formAttr'] = $formAttr;
}
<?php if (!isset($this->indexStart)) $this->indexStart = 0; ?>
<?php $i = $this->indexStart; ?>
<?php $listStart = $this->results->getStartRecord() + $i - $this->indexStart; ?>
<ol class="record-list" start="<?=$listStart?>">
<?php foreach ($this->results->getResults() as $current): ?>
<?php $recordNumber = $this->results->getStartRecord() + $i - $this->indexStart; ?>
<li id="result<?=$i++ ?>" class="result<?=$current->supportsAjaxStatus()?' ajaxItem':''?>">
<?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?>
<?=$this->record($current)->getCheckbox('', 'search-cart-form', $recordNumber)?>
<?php endif; ?>
<div class="record-number">
<?=$recordNumber ?>
</div>
<?=$this->record($current)->getSearchResult('list')?>
</li>
<?php endforeach; ?>
</ol>
$__vars = $this->vars()->getArrayCopy();
if (array_key_exists('this', $__vars)) {
unset($__vars['this']);
}
extract($__vars);
unset($__vars); // remove $__vars from local scope
$this->__content = '';
while ($this->__template = array_pop($this->__templates)) {
$this->__file = $this->resolver($this->__template);
if (! $this->__file) {
throw new Exception\RuntimeException(sprintf(
'%s: Unable to render template "%s"; resolver could not resolve to a file',
__METHOD__,
$this->__template
));
}
try {
ob_start();
$includeReturn = include $this->__file;
$this->__content = ob_get_clean();
} catch (\Throwable $ex) {
ob_end_clean();
throw $ex;
} catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced
ob_end_clean();
throw $ex;
}
if ($includeReturn === false && empty($this->__content)) {
throw new Exception\UnexpectedValueException(sprintf(
'%s: Unable to render template "%s"; file include failed',
__METHOD__,
$this->__file
));
}
}
$this->setVars(array_pop($this->__varsCache));
if ($this->__filterChain instanceof FilterChain) {
$this->layout()->srmessage = $emptyMessage;
echo $emptyMessage;
?>
</p>
<?php if (isset($this->parseError)): ?>
<p class="alert alert-danger"><?=$this->transEsc('nohit_parse_error')?></p>
<?php endif; ?>
<?php foreach (($top = $this->results->getRecommendations('top')) as $index => $current): ?>
<?=$this->recommend($current, 'top', $index)?>
<?php endforeach; ?>
<?php foreach ($this->results->getRecommendations('noresults') as $index => $current): ?>
<?php if (!in_array($current, $top)): ?>
<?=$this->recommend($current, 'noresults', $index)?>
<?php endif; ?>
<?php endforeach; ?>
<?php else: ?>
<form id="search-cart-form" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler">
<?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => ''])?>
</form>
<?=$this->render('search/list-' . $this->params->getView() . '.phtml')?>
<?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => 'bottom_', 'formAttr' => 'search-cart-form'])?>
<?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results, 'options' => $this->paginationOptions ?? []])?>
<?=$this->context($this)->renderInContext('search/controls/results-tools.phtml', ['results' => $this->results])?>
<?php endif; ?>
</div>
<?php /* End Main Listing */ ?>
<?php /* Narrow Search Options */ ?>
<div class="<?=$this->layoutClass('sidebar')?>" id="search-sidebar">
<?php foreach ($this->results->getRecommendations('side') as $index => $current): ?>
<?=$this->recommend($current, 'side', $index)?>
<?php endforeach; ?>
</div>
<?php /* End Narrow Search Options */ ?>
$__vars = $this->vars()->getArrayCopy();
if (array_key_exists('this', $__vars)) {
unset($__vars['this']);
}
extract($__vars);
unset($__vars); // remove $__vars from local scope
$this->__content = '';
while ($this->__template = array_pop($this->__templates)) {
$this->__file = $this->resolver($this->__template);
if (! $this->__file) {
throw new Exception\RuntimeException(sprintf(
'%s: Unable to render template "%s"; resolver could not resolve to a file',
__METHOD__,
$this->__template
));
}
try {
ob_start();
$includeReturn = include $this->__file;
$this->__content = ob_get_clean();
} catch (\Throwable $ex) {
ob_end_clean();
throw $ex;
} catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced
ob_end_clean();
throw $ex;
}
if ($includeReturn === false && empty($this->__content)) {
throw new Exception\UnexpectedValueException(sprintf(
'%s: Unable to render template "%s"; file include failed',
__METHOD__,
$this->__file
));
}
}
$this->setVars(array_pop($this->__varsCache));
if ($this->__filterChain instanceof FilterChain) {
// If EVENT_RENDERER or EVENT_RENDERER_POST changed the model, make sure
// we use this new model instead of the current $model
$model = $event->getModel();
// If we have children, render them first, but only if:
// a) the renderer does not implement TreeRendererInterface, or
// b) it does, but canRenderTrees() returns false
if ($model->hasChildren()
&& (! $renderer instanceof TreeRendererInterface
|| ! $renderer->canRenderTrees())
) {
$this->renderChildren($model);
}
// Reset the model, in case it has changed, and set the renderer
$event->setModel($model);
$event->setRenderer($renderer);
$rendered = $renderer->render($model);
// If this is a child model, return the rendered content; do not
// invoke the response strategy.
$options = $model->getOptions();
if (array_key_exists('has_parent', $options) && $options['has_parent']) {
return $rendered;
}
$event->setResult($rendered);
$event->setName(ViewEvent::EVENT_RESPONSE);
$events->triggerEvent($event);
}
/**
* Loop through children, rendering each
*
* @param Model $model
* @throws Exception\DomainException
* @return void
$event->setName(ViewEvent::EVENT_RESPONSE);
$events->triggerEvent($event);
}
/**
* Loop through children, rendering each
*
* @param Model $model
* @throws Exception\DomainException
* @return void
*/
protected function renderChildren(Model $model)
{
foreach ($model as $child) {
if ($child->terminate()) {
throw new Exception\DomainException('Inconsistent state; child view model is marked as terminal');
}
$child->setOption('has_parent', true);
$result = $this->render($child);
$child->setOption('has_parent', null);
$capture = $child->captureTo();
if (! empty($capture)) {
if ($child->isAppend()) {
$oldResult = $model->{$capture};
$model->setVariable($capture, $oldResult . $result);
} else {
$model->setVariable($capture, $result);
}
}
}
}
/**
* Create and return ViewEvent used by render()
*
* @return ViewEvent
*/
protected function getEvent()
{
__METHOD__
));
}
$event->setRenderer($renderer);
$event->setName(ViewEvent::EVENT_RENDERER_POST);
$events->triggerEvent($event);
// If EVENT_RENDERER or EVENT_RENDERER_POST changed the model, make sure
// we use this new model instead of the current $model
$model = $event->getModel();
// If we have children, render them first, but only if:
// a) the renderer does not implement TreeRendererInterface, or
// b) it does, but canRenderTrees() returns false
if ($model->hasChildren()
&& (! $renderer instanceof TreeRendererInterface
|| ! $renderer->canRenderTrees())
) {
$this->renderChildren($model);
}
// Reset the model, in case it has changed, and set the renderer
$event->setModel($model);
$event->setRenderer($renderer);
$rendered = $renderer->render($model);
// If this is a child model, return the rendered content; do not
// invoke the response strategy.
$options = $model->getOptions();
if (array_key_exists('has_parent', $options) && $options['has_parent']) {
return $rendered;
}
$event->setResult($rendered);
$event->setName(ViewEvent::EVENT_RESPONSE);
$events->triggerEvent($event);
}
if ($result instanceof Response) {
return $result;
}
// Martial arguments
$request = $e->getRequest();
$response = $e->getResponse();
$viewModel = $e->getViewModel();
if (! $viewModel instanceof ViewModel) {
return;
}
$view = $this->view;
$view->setRequest($request);
$view->setResponse($response);
$caughtException = null;
try {
$view->render($viewModel);
} catch (\Throwable $ex) {
$caughtException = $ex;
} catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced
$caughtException = $ex;
}
if ($caughtException !== null) {
if ($e->getName() === MvcEvent::EVENT_RENDER_ERROR) {
throw $caughtException;
}
$application = $e->getApplication();
$events = $application->getEventManager();
$e->setError(Application::ERROR_EXCEPTION);
$e->setParam('exception', $caughtException);
$e->setName(MvcEvent::EVENT_RENDER_ERROR);
$events->triggerEvent($e);
}
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
return $this->completeRequest($event);
}
/**
* Complete the request
*
* Triggers "render" and "finish" events, and returns response from
* event object.
*
* @param MvcEvent $event
* @return Application
*/
protected function completeRequest(MvcEvent $event)
{
$events = $this->events;
$event->setTarget($this);
$event->setName(MvcEvent::EVENT_RENDER);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$event->setName(MvcEvent::EVENT_FINISH);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
return $this;
}
}
// Trigger dispatch event
$event->setName(MvcEvent::EVENT_DISPATCH);
$event->stopPropagation(false); // Clear before triggering
$result = $events->triggerEventUntil($shortCircuit, $event);
// Complete response
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setName(MvcEvent::EVENT_FINISH);
$event->setTarget($this);
$event->setResponse($response);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$this->response = $response;
return $this;
}
$response = $this->response;
$event->setResponse($response);
return $this->completeRequest($event);
}
/**
* Complete the request
*
* Triggers "render" and "finish" events, and returns response from
* event object.
*
* @param MvcEvent $event
* @return Application
*/
protected function completeRequest(MvcEvent $event)
{
$events = $this->events;
$event->setTarget($this);
$event->setName(MvcEvent::EVENT_RENDER);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$pathParts[] = APPLICATION_PATH . '/vendor';
$pathParts[] = get_include_path();
set_include_path(implode(PATH_SEPARATOR, $pathParts));
// Composer autoloading
if (file_exists('vendor/autoload.php')) {
$loader = include 'vendor/autoload.php';
}
if (!class_exists('Laminas\Loader\AutoloaderFactory')) {
throw new RuntimeException('Unable to load Laminas autoloader.');
}
// Run the application!
$app = Laminas\Mvc\Application::init(require 'config/application.config.php');
if (PHP_SAPI === 'cli') {
return $app->getServiceManager()
->get(\VuFindConsole\ConsoleRunner::class)->run();
} else {
$app->run();
}