Browse Source

Fix Bug #985492: faceted search feature

pull/1/head
Sebastian Meyer 11 years ago
parent
commit
d222dd6040
  1. 16
      dlf/cli/class.tx_dlf_cli.php
  2. 60
      dlf/common/class.tx_dlf_document.php
  3. 22
      dlf/common/class.tx_dlf_format.php
  4. 117
      dlf/common/class.tx_dlf_helper.php
  5. 10
      dlf/common/class.tx_dlf_indexing.php
  6. 16
      dlf/common/class.tx_dlf_mods.php
  7. 20
      dlf/common/class.tx_dlf_module.php
  8. 20
      dlf/common/class.tx_dlf_plugin.php
  9. 601
      dlf/common/class.tx_dlf_solr.php
  10. 15
      dlf/common/locallang.xml
  11. 4
      dlf/ext_tables.php
  12. 55
      dlf/hooks/class.tx_dlf_tceforms.php
  13. 10
      dlf/hooks/class.tx_dlf_tcemain.php
  14. 3
      dlf/plugins/collection/class.tx_dlf_collection.php
  15. 412
      dlf/plugins/search/class.tx_dlf_search.php
  16. 16
      dlf/plugins/search/flexform.xml
  17. 4
      dlf/plugins/search/locallang.xml
  18. 28
      dlf/plugins/search/setup.txt
  19. 1
      dlf/plugins/search/template.tmpl
  20. 54
      dlf/plugins/toc/setup.txt
  21. 2
      dlf/tca.php

16
dlf/cli/class.tx_dlf_cli.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2012 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -106,13 +106,13 @@ class tx_dlf_cli extends t3lib_cli {
// Set basic information about the script.
$this->cli_help = array (
'name' => 'Command Line Interface for Goobi.Presentation',
'synopsis' => '###OPTIONS###',
'description' => 'Currently the only task available is "index".'.LF.'Try "/PATH/TO/TYPO3/cli_dispatch.phpsh dlf index" to view more options.',
'examples' => '/PATH/TO/TYPO3/cli_dispatch.phpsh dlf TASK -ARG1=VALUE1 -ARG2=VALUE2',
'options' => '',
'license' => 'GNU GPL - free software!',
'author' => 'Sebastian Meyer <sebastian.meyer@slub-dresden.de>',
'name' => 'Command Line Interface for Goobi.Presentation',
'synopsis' => '###OPTIONS###',
'description' => 'Currently the only task available is "index".'.LF.'Try "/PATH/TO/TYPO3/cli_dispatch.phpsh dlf index" to view more options.',
'examples' => '/PATH/TO/TYPO3/cli_dispatch.phpsh dlf TASK -ARG1=VALUE1 -ARG2=VALUE2',
'options' => '',
'license' => 'GNU GPL - free software!',
'author' => 'Sebastian Meyer <sebastian.meyer@slub-dresden.de>',
);
// Run parent constructor.

60
dlf/common/class.tx_dlf_document.php

@ -180,7 +180,7 @@ final class tx_dlf_document {
* @var integer
* @access protected
*/
protected $parentid = 0;
protected $parentId = 0;
/**
* This holds the physical pages
@ -229,7 +229,7 @@ final class tx_dlf_document {
* @var string
* @access protected
*/
protected $recordid;
protected $recordId;
/**
* This holds the singleton object of each document with its UID as array key
@ -658,32 +658,34 @@ final class tx_dlf_document {
$this->_getDmdSec();
// Is this metadata format supported?
if (!empty($this->formats[$this->dmdSec[$dmdId]['type']]['class'])) {
if (!empty($this->formats[$this->dmdSec[$dmdId]['type']])) {
$class = $this->formats[$this->dmdSec[$dmdId]['type']]['class'];
if (!empty($this->formats[$this->dmdSec[$dmdId]['type']]['class'])) {
} else {
$class = $this->formats[$this->dmdSec[$dmdId]['type']]['class'];
if (TYPO3_DLOG) {
// Get the metadata from class.
if (class_exists($class) && ($obj = t3lib_div::makeInstance($class)) instanceof tx_dlf_format) {
t3lib_div::devLog('[tx_dlf_document->getMetadata('.$id.', '.$_cPid.')] Unsupported metadata format "'.$this->dmdSec[$dmdId]['type'].'" in dmdSec with @ID "'.$dmdId.'"', $this->extKey, SYSLOG_SEVERITY_WARNING);
$obj->extractMetadata($this->dmdSec[$dmdId]['xml'], $metadata);
}
} else {
return array ();
if (TYPO3_DLOG) {
}
t3lib_div::devLog('[tx_dlf_document->getMetadata('.$id.', '.$_cPid.')] Invalid class/method "'.$class.'->extractMetadata()" for metadata format "'.$this->dmdSec[$dmdId]['type'].'"', $this->extKey, SYSLOG_SEVERITY_WARNING);
// Get the metadata from class.
if (class_exists($class) && ($obj = t3lib_div::makeInstance($class)) instanceof tx_dlf_format) {
}
$obj->extractMetadata($this->dmdSec[$dmdId]['xml'], $metadata);
}
}
} else {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_document->getMetadata('.$id.', '.$_cPid.')] Invalid class/method "'.$class.'->extractMetadata()" for metadata format "'.$this->dmdSec[$dmdId]['type'].'"', $this->extKey, SYSLOG_SEVERITY_ERROR);
t3lib_div::devLog('[tx_dlf_document->getMetadata('.$id.', '.$_cPid.')] Unsupported metadata format "'.$this->dmdSec[$dmdId]['type'].'" in dmdSec with @ID "'.$dmdId.'"', $this->extKey, SYSLOG_SEVERITY_WARNING);
}
@ -880,7 +882,7 @@ final class tx_dlf_document {
$titledata = $this->getMetadata($this->_getToplevelId(), $cPid);
// Set record identifier for METS file.
array_unshift($titledata['record_id'], $this->recordid);
array_unshift($titledata['record_id'], $this->recordId);
return $titledata;
@ -1432,7 +1434,7 @@ final class tx_dlf_document {
$this->pid = $pid;
$this->parentid = $partof;
$this->parentId = $partof;
}
@ -1606,15 +1608,15 @@ final class tx_dlf_document {
}
/**
* This returns $this->parentid via __get()
* This returns $this->parentId via __get()
*
* @access protected
*
* @return integer The UID of the parent document or zero if not applicable
*/
protected function _getParentid() {
protected function _getParentId() {
return $this->parentid;
return $this->parentId;
}
@ -1775,15 +1777,15 @@ final class tx_dlf_document {
}
/**
* This returns $this->recordid via __get()
* This returns $this->recordId via __get()
*
* @access protected
*
* @return mixed The METS file's record identifier
*/
protected function _getRecordid() {
protected function _getRecordId() {
return $this->recordid;
return $this->recordId;
}
@ -1963,7 +1965,7 @@ final class tx_dlf_document {
if (!empty($objId[0]['OBJID'])) {
$this->recordid = (string) $objId[0]['OBJID'];
$this->recordId = (string) $objId[0]['OBJID'];
}
@ -1974,7 +1976,7 @@ final class tx_dlf_document {
if (method_exists($hookObj, 'construct_postProcessRecordId')) {
$this->recordid = $hookObj->construct_postProcessRecordId($xml, $this->recordid);
$this->recordId = $hookObj->construct_postProcessRecordId($xml, $this->recordId);
}
@ -1982,9 +1984,9 @@ final class tx_dlf_document {
}
if (!empty($this->recordid)) {
if (!empty($this->recordId)) {
$whereClause = 'tx_dlf_documents.record_id='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->recordid, 'tx_dlf_documents').tx_dlf_helper::whereClause('tx_dlf_documents');
$whereClause = 'tx_dlf_documents.record_id='.$GLOBALS['TYPO3_DB']->fullQuoteStr($this->recordId, 'tx_dlf_documents').tx_dlf_helper::whereClause('tx_dlf_documents');
} else {
@ -2014,7 +2016,7 @@ final class tx_dlf_document {
if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
list ($this->uid, $this->pid, $this->recordid, $this->parentid, $location) = $GLOBALS['TYPO3_DB']->sql_fetch_row($result);
list ($this->uid, $this->pid, $this->recordId, $this->parentId, $location) = $GLOBALS['TYPO3_DB']->sql_fetch_row($result);
// Load XML file...
if ($this->load($location)) {
@ -2122,7 +2124,7 @@ final class tx_dlf_document {
// SimpleXMLElement objects can't be serialized, thus save the XML as string for serialization
$this->asXML = $this->xml->asXML();
return array ('uid', 'pid', 'parentid', 'asXML');
return array ('uid', 'pid', 'parentId', 'asXML');
}
@ -2187,7 +2189,7 @@ final class tx_dlf_document {
}
/* No xclasses allowed for this class!
/* No xclasses allowed for final classes!
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_document.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_document.php']);
}

22
dlf/common/class.tx_dlf_format.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -28,14 +28,14 @@
/**
* Interface 'tx_dlf_format' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
interface tx_dlf_format {
/**
@ -53,8 +53,8 @@ interface tx_dlf_format {
}
/* No xclasses for interfaces!
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_format.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_format.php']);
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_format.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_format.php']);
}
*/

117
dlf/common/class.tx_dlf_helper.php

@ -46,22 +46,12 @@ class tx_dlf_helper {
public static $extKey = 'dlf';
/**
* The encryption key
* @see encrypt() / decrypt()
* The locallang array for common use
*
* @var string
* @access private
*/
private static $ENCRYPTION_KEY = 'b8b311560d3e6f8dea0aa445995b1b2b';
/**
* The initialization vector for encryption
* @see encrypt() / decrypt()
*
* @var string
* @access private
* @var array
* @access protected
*/
private static $ENCRYPTION_IV = '381416de30a5c970f8f486aa6d5cc932';
protected static $locallang = array ();
/**
* Searches the array recursively for a given value and returns the corresponding key if successful
@ -253,9 +243,21 @@ class tx_dlf_helper {
}
$iv = substr(self::$ENCRYPTION_IV, 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB));
if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_helper->decrypt('.$encrypted.', '.$hash.')] No encryption key set in TYPO3 configuration', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
return;
}
$iv = substr(md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB));
$decrypted = mcrypt_decrypt(MCRYPT_BLOWFISH, self::$ENCRYPTION_KEY, base64_decode($encrypted), MCRYPT_MODE_CFB, $iv);
$decrypted = mcrypt_decrypt(MCRYPT_BLOWFISH, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], base64_decode($encrypted), MCRYPT_MODE_CFB, $iv);
$salt = substr($hash, 0, 10);
@ -302,9 +304,21 @@ class tx_dlf_helper {
}
$iv = substr(self::$ENCRYPTION_IV, 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB));
if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_BLOWFISH, self::$ENCRYPTION_KEY, $string, MCRYPT_MODE_CFB, $iv));
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_helper->encrypt('.$string.')] No encryption key set in TYPO3 configuration', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
return;
}
$iv = substr(md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CFB));
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_BLOWFISH, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $string, MCRYPT_MODE_CFB, $iv));
$salt = substr(md5(uniqid(rand(), TRUE)), 0, 10);
@ -330,7 +344,7 @@ class tx_dlf_helper {
return $GLOBALS['BE_USER'];
} elseif (!isset($_COOKIE['be_typo_user'])) {
} elseif (!isset($_COOKIE['be_typo_user'])) { // TODO: Since TYPO3 4.6 the cookie name is configurable in $TYPO3_CONF_VARS['BE']['cookieName']
// Initialize backend session with CLI user's rights.
$userObj = t3lib_div::makeInstance('t3lib_beUserAuth');
@ -530,6 +544,71 @@ class tx_dlf_helper {
}
/**
* Wrapper function for getting localizations in frontend and backend
*
* @param string $key: The locallang key to translate
* @param string $default: Default return value if no translation is available
* @param boolean $hsc: Should the result be htmlspecialchar()'ed?
*
* @return string The translated string or the given key on failure
*/
public static function getLL($key, $default = '', $hsc = FALSE) {
// Set initial output to default value.
$translated = (string) $default;
// Load common locallang file.
if (empty(self::$locallang)) {
$file = t3lib_extMgm::extPath(self::$extKey, 'common/locallang.xml');
if (TYPO3_MODE === 'FE') {
self::$locallang = $GLOBALS['TSFE']->readLLfile($file);
} elseif (TYPO3_MODE === 'BE') {
self::$locallang = $GLOBALS['LANG']->includeLLFile($file, FALSE, TRUE);
} elseif (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_helper->getLL('.$key.', '.$default.', ['.($hsc ? 'TRUE' : 'FALSE').'])] Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
}
// Get translation.
if (!empty(self::$locallang['default'][$key])) {
if (TYPO3_MODE === 'FE') {
$translated = $GLOBALS['TSFE']->getLLL($key, self::$locallang);
} elseif (TYPO3_MODE === 'BE') {
$translated = $GLOBALS['LANG']->getLLL($key, self::$locallang, FALSE);
} elseif (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_helper->getLL('.$key.', '.$default.', ['.($hsc ? 'TRUE' : 'FALSE').'])] Unexpected TYPO3_MODE "'.TYPO3_MODE.'"', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
}
// Escape HTML characters if applicable.
if ($hsc) {
$translated = htmlspecialchars($translated);
}
return $translated;
}
/**
* Get the URN of an object
* @see http://www.persistent-identifier.de/?link=316

10
dlf/common/class.tx_dlf_indexing.php

@ -150,7 +150,7 @@ class tx_dlf_indexing {
}
self::$solr->commit();
self::$solr->service->commit();
// Get document title from database.
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
@ -271,9 +271,9 @@ class tx_dlf_indexing {
try {
// Delete Solr document.
self::$solr->deleteByQuery('uid:'.$uid);
self::$solr->service->deleteByQuery('uid:'.$uid);
self::$solr->commit();
self::$solr->service->commit();
} catch (Exception $e) {
@ -539,7 +539,7 @@ class tx_dlf_indexing {
try {
self::$solr->addDocument($solrDoc);
self::$solr->service->addDocument($solrDoc);
} catch (Exception $e) {
@ -602,7 +602,7 @@ class tx_dlf_indexing {
if (!self::$solr) {
// Connect to Solr server.
if (self::$solr = tx_dlf_solr::solrConnect($core)) {
if (self::$solr = tx_dlf_solr::getInstance($core)) {
// Load indexing configuration if needed.
if ($pid) {

16
dlf/common/class.tx_dlf_mods.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -28,13 +28,13 @@
/**
* Metadata format class 'tx_dlf_mods' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
*/
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
*/
class tx_dlf_mods implements tx_dlf_format {
/**

20
dlf/common/class.tx_dlf_module.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -28,14 +28,14 @@
/**
* Base class 'tx_dlf_module' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
abstract class tx_dlf_module extends t3lib_SCbase {
public $extKey = 'dlf';
@ -232,7 +232,7 @@ abstract class tx_dlf_module extends t3lib_SCbase {
/* No xclasses for abstract classes!
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_module.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_module.php']);
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_module.php']);
}
*/

20
dlf/common/class.tx_dlf_plugin.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -28,14 +28,14 @@
/**
* Base class 'tx_dlf_plugin' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
* @abstract
*/
abstract class tx_dlf_plugin extends tslib_pibase {
public $extKey = 'dlf';
@ -317,7 +317,7 @@ abstract class tx_dlf_plugin extends tslib_pibase {
/* No xclasses for abstract classes!
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_plugin.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_plugin.php']);
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_plugin.php']);
}
*/

601
dlf/common/class.tx_dlf_solr.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -24,27 +24,158 @@
// TODO: Clean up and reduce code duplication. Consider switching to Solarium.
/**
* [CLASS/FUNCTION INDEX of SCRIPT]
*/
*/
/**
* Solr class 'tx_dlf_solr' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @author Henrik Lochmann <dev@mentalmotive.com>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
*/
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @author Henrik Lochmann <dev@mentalmotive.com>
* @copyright Copyright (c) 2011, Sebastian Meyer, SLUB Dresden
* @package TYPO3
* @subpackage tx_dlf
* @access public
*/
class tx_dlf_solr {
/**
* This holds the core name
*
* @var string
* @access protected
*/
protected $core = '';
/**
* This holds the PID for the configuration
*
* @var integer
* @access protected
*/
protected $cPid = 0;
/**
* The extension key
*
* @var string
* @access public
*/
public static $extKey = 'dlf';
public $extKey = 'dlf';
/**
* This holds the filter query
*
* @var array
* @access protected
*/
protected $filter = array ();
/**
* This holds the max results
*
* @var integer
* @access protected
*/
protected $limit = 50000;
/**
* This holds the number of hits for last search
*
* @var integer
* @access protected
*/
protected $numberOfHits = 0;
/**
* Is the search instantiated successfully?
*
* @var boolean
* @access protected
*/
protected $ready = FALSE;
/**
* This holds the singleton search objects with their core as array key
*
* @var array(tx_dlf_solr)
* @access protected
*/
protected static $registry = array ();
/**
* This holds the Solr service object
*
* @var Apache_Solr_Service
* @access protected
*/
protected $service;
/**
* This is a singleton class, thus instances must be created by this method
*
* @access public
*
* @param mixed $core: Name or UID of the core to load
*
* @return tx_dlf_solr Instance of this class
*/
public static function getInstance($core) {
// Save parameter for logging purposes.
$_core = $core;
// Get core name if UID is given.
if (t3lib_div::testInt($core)) {
$core = tx_dlf_helper::getIndexName($core, 'tx_dlf_solrcores');
}
// Check if core is set.
if (empty($core)) {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_solr->getInstance('.$_core.')] Invalid core name "'.$core.'" for Apache Solr', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
return;
}
// Check if there is an instance in the registry already.
if (is_object(self::$registry[$core]) && self::$registry[$core] instanceof self) {
// Return singleton instance if available.
return self::$registry[$core];
}
// Create new instance...
$instance = new self($core);
// ...and save it to registry.
if ($instance->ready) {
self::$registry[$core] = $instance;
// Return new instance.
return $instance;
} else {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_solr->getInstance('.$_core.')] Could not connect to Apache Solr server', $this->extKey, SYSLOG_SEVERITY_ERROR);
}
return;
}
}
/**
* Returns the request URL for a specific Solr core
@ -82,128 +213,478 @@ class tx_dlf_solr {
}
/**
* Get SolrPhpClient service object and establish connection to Solr server
* @see EXT:dlf/lib/SolrPhpClient/Apache/Solr/Service.php
* Get next unused Solr core number
*
* @access public
*
* @param mixed $core: Name or UID of the core to load
* @param integer $start: Number to start with
*
* @return mixed Instance of Apache_Solr_Service or NULL on failure
* @return integer First unused core number found
*/
public static function solrConnect($core = 0) {
public static function solrGetCoreNumber($start = 0) {
// Save parameter for logging purposes.
$_core = $core;
$start = max(intval($start), 0);
// Load class.
if (!class_exists('Apache_Solr_Service')) {
// Check if core already exists.
if (self::getInstance('dlfCore'.$start) === NULL) {
require_once(t3lib_div::getFileAbsFileName('EXT:'.self::$extKey.'/lib/SolrPhpClient/Apache/Solr/Service.php'));
return $start;
} else {
return self::solrGetCoreNumber($start + 1);
}
// Get Solr credentials.
$conf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][self::$extKey]);
}
$host = ($conf['solrHost'] ? $conf['solrHost'] : 'localhost');
/**
* Processes a search request.
*
* @access public
*
* @param string $query: The search query
*
* @return tx_dlf_list The result list
*/
public function search($query = '*') {
// Prepend username and password to hostname.
if ($conf['solrUser'] && $conf['solrPass']) {
// Sanitize query string.
$query = (string) $query;
$host = $conf['solrUser'].':'.$conf['solrPass'].'@'.$host;
if (empty($query)) {
$query = '*';
}
// Set port if not set.
$port = t3lib_div::intInRange($conf['solrPort'], 1, 65535, 8180);
// Perform search.
$query = $this->service->search($query, 0, $this->limit, $this->filter);
// Get core name if UID is given.
if (t3lib_div::testInt($core)) {
$this->numberOfHits = count($query->response->docs);
$core = tx_dlf_helper::getIndexName($core, 'tx_dlf_solrcores');
$toplevel = array ();
if (empty($core)) {
$checks = array ();
if (TYPO3_DLOG) {
// Get metadata configuration.
if ($numHits) {
t3lib_div::devLog('[tx_dlf_solr->solrConnect('.$_core.')] Invalid UID "'.$_core.'" for Apache Solr core', $this->extKey, SYSLOG_SEVERITY_ERROR);
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'tx_dlf_metadata.index_name AS index_name,tx_dlf_metadata.tokenized AS tokenized,tx_dlf_metadata.indexed AS indexed,tx_dlf_metadata.is_listed AS is_listed,tx_dlf_metadata.is_sortable AS is_sortable',
'tx_dlf_metadata',
'(tx_dlf_metadata.is_listed=1 OR tx_dlf_metadata.is_sortable=1) AND tx_dlf_metadata.pid='.intval($this->cPid).tx_dlf_helper::whereClause('tx_dlf_metadata'),
'',
'tx_dlf_metadata.sorting ASC',
''
);
$metadata = array ();
$sorting = array ();
while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
if ($resArray['is_listed']) {
$metadata[$resArray['index_name']] = $resArray['index_name'].'_'.($resArray['tokenized'] ? 't' : 'u').'s'.($resArray['indexed'] ? 'i' : 'u');
}
return;
if ($resArray['is_sortable']) {
$sorting[$resArray['index_name']] = $resArray['index_name'].'_sorting';
}
}
}
// Append core name to path.
$path = trim($conf['solrPath'], '/').'/'.$core;
// Keep track of relevance.
$i = 0;
// Instantiate Apache_Solr_Service class.
$solr = t3lib_div::makeInstance('Apache_Solr_Service', $host, $port, $path);
// Process results.
foreach ($query->response->docs as $doc) {
// Check if connection is established.
if ($solr->ping() !== FALSE) {
// Prepate document's metadata.
$docMeta = array ();
// Do not collapse single value arrays.
$solr->setCollapseSingleValueArrays = FALSE;
foreach ($metadata as $index_name => $solr_name) {
return $solr;
if (!empty($doc->$solr_name)) {
} else {
$docMeta[$index_name] = (is_array($doc->$solr_name) ? $doc->$solr_name : array ($doc->$solr_name));
}
}
// Prepate document's metadata for sorting.
$docSorting = array ();
foreach ($sorting as $index_name => $solr_name) {
if (!empty($doc->$solr_name)) {
$docSorting[$index_name] = (is_array($doc->$solr_name) ? $doc->$solr_name[0] : $doc->$solr_name);
}
}
// Add relevance to sorting values.
$docSorting['relevance'] = str_pad($i, 6, '0', STR_PAD_LEFT);
// Split toplevel documents from subparts.
if ($doc->toplevel == 1) {
$toplevel[$doc->uid] = array (
'uid' => $doc->uid,
'page' => $doc->page,
'metadata' => $docMeta,
'sorting' => $docSorting,
'subparts' => (!empty($toplevel[$doc->uid]['subparts']) ? $toplevel[$doc->uid]['subparts'] : array ())
);
} else {
$toplevel[$doc->uid]['subparts'][] = array (
'uid' => $doc->uid,
'page' => $doc->page,
'metadata' => $docMeta,
'sorting' => $docSorting
);
if (!in_array($doc->uid, $check)) {
$checks[] = $doc->uid;
}
}
$i++;
}
// Check if the toplevel documents have metadata.
foreach ($checks as $check) {
if (empty($toplevel[$check]['uid'])) {
// Get information for toplevel document.
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'tx_dlf_documents.uid AS uid,tx_dlf_documents.metadata AS metadata,tx_dlf_documents.metadata_sorting AS metadata_sorting',
'tx_dlf_documents',
'tx_dlf_documents.uid='.intval($check).tx_dlf_helper::whereClause('tx_dlf_documents'),
'',
'',
'1'
);
// Process results.
if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
$resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
// Prepare document's metadata.
$metadata = unserialize($resArray['metadata']);
if (!empty($metadata['type'][0]) && t3lib_div::testInt($metadata['type'][0])) {
$metadata['type'][0] = tx_dlf_helper::getIndexName($metadata['type'][0], 'tx_dlf_structures', $this->cPid);
}
if (!empty($metadata['owner'][0]) && t3lib_div::testInt($metadata['owner'][0])) {
$metadata['owner'][0] = tx_dlf_helper::getIndexName($metadata['owner'][0], 'tx_dlf_libraries', $this->cPid);
}
if (!empty($metadata['collection']) && is_array($metadata['collection'])) {
foreach ($metadata['collection'] as $i => $collection) {
if (t3lib_div::testInt($collection)) {
$metadata['collection'][$i] = tx_dlf_helper::getIndexName($metadata['collection'][$i], 'tx_dlf_collections', $this->cPid);
}
}
}
// Prepare document's metadata for sorting.
$sorting = unserialize($resArray['metadata_sorting']);
if (!empty($sorting['type']) && t3lib_div::testInt($sorting['type'])) {
$sorting['type'] = tx_dlf_helper::getIndexName($sorting['type'], 'tx_dlf_structures', $this->cPid);
}
if (!empty($sorting['owner']) && t3lib_div::testInt($sorting['owner'])) {
$sorting['owner'] = tx_dlf_helper::getIndexName($sorting['owner'], 'tx_dlf_libraries', $this->cPid);
}
if (!empty($sorting['collection']) && t3lib_div::testInt($sorting['collection'])) {
$sorting['collection'] = tx_dlf_helper::getIndexName($sorting['collection'], 'tx_dlf_collections', $this->cPid);
}
$toplevel[$check] = array (
'uid' => $resArray['uid'],
'page' => 1,
'metadata' => $metadata,
'sorting' => $sorting,
'subparts' => $toplevel[$check]['subparts']
);
} else {
// Clear entry if there is no (accessible) toplevel document.
unset ($toplevel[$check]);
}
}
}
// Save list of documents.
$list = t3lib_div::makeInstance('tx_dlf_list');
$list->reset();
$list->add(array_values($toplevel));
// Set metadata for search.
$list->metadata = array (
'label' => '',
'description' => '',
'options' => array (
'source' => 'search',
'select' => $query,
'filter' => $this->filter,
'order' => 'relevance'
)
);
return $list;
}
/**
* This returns $this->numberOfHits via __get()
*
* @access protected
*
* @return integer Total number of hits for last search
*/
protected function _getNumberOfHits() {
return $this->numberOfHits;
}
/**
* This returns $this->ready via __get()
*
* @access protected
*
* @return boolean Is the search instantiated successfully?
*/
protected function _getReady() {
return $this->ready;
}
/**
* This returns $this->service via __get()
*
* @access protected
*
* @return Apache_Solr_Service Apache Solr service object
*/
protected function _getService() {
return $this->service;
}
/**
* This sets $this->cPid via __set()
*
* @access protected
*
* @param integer $value: The new PID for the metadata definitions
*
* @return void
*/
protected function _setCPid($value) {
$this->cPid = max(intval($value), 0);
}
/**
* This sets $this->filter via __set()
*
* @access protected
*
* @param array $value: The filter query
*
* @return void
*/
protected function _setFilter(array $value) {
$this->filter = $value;
}
/**
* This sets $this->limit via __set()
*
* @access protected
*
* @param integer $value: The max number of results
*
* @return void
*/
protected function _setLimit($value) {
$this->limit = max(intval($value), 0);
}
/**
* This magic method is called each time an invisible property is referenced from the object
*
* @access public
*
* @param string $var: Name of variable to get
*
* @return mixed Value of $this->$var
*/
public function __get($var) {
$method = '_get'.ucfirst($var);
if (!property_exists($this, $var) || !method_exists($this, $method)) {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_solr->solrConnect('.$_core.')] Could not connect to Apache Solr server with core "'.$core.'"', $this->extKey, SYSLOG_SEVERITY_ERROR);
t3lib_div::devLog('[tx_dlf_solr->__get('.$var.')] There is no getter function for property "'.$var.'"', $this->extKey, SYSLOG_SEVERITY_WARNING);
}
return;
} else {
return $this->$method();
}
}
/**
* Get next unused Solr core number
* This magic method is called each time an invisible property is referenced from the object
*
* @access public
*
* @param integer $start: Number to start with
* @param string $var: Name of variable to set
* @param mixed $value: New value of variable
*
* @return integer First unused core number found
* @return void
*/
public static function solrGetCoreNumber($start = 0) {
public function __set($var, $value) {
$start = max(intval($start), 0);
$method = '_set'.ucfirst($var);
// Check if core already exists.
if (self::solrConnect('dlfCore'.$start) === NULL) {
if (!property_exists($this, $var) || !method_exists($this, $method)) {
return $start;
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_solr->__set('.$var.', '.$value.')] There is no setter function for property "'.$var.'"', $this->extKey, SYSLOG_SEVERITY_WARNING);
}
} else {
return self::solrGetCoreNumber($start + 1);
$this->$method($value);
}
}
/**
* This is a static class, thus no instances should be created
* This is a singleton class, thus the constructor should be private/protected
*
* @access protected
*
* @param string $core: The name of the core to use
*
* @return void
*/
protected function __construct() {
protected function __construct($core) {
// Load class.
if (!class_exists('Apache_Solr_Service')) {
require_once(t3lib_div::getFileAbsFileName('EXT:'.self::$extKey.'/lib/SolrPhpClient/Apache/Solr/Service.php'));
}
// Get Solr credentials.
$conf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][self::$extKey]);
$host = ($conf['solrHost'] ? $conf['solrHost'] : 'localhost');
// Prepend username and password to hostname.
if ($conf['solrUser'] && $conf['solrPass']) {
$host = $conf['solrUser'].':'.$conf['solrPass'].'@'.$host;
}
// Set port if not set.
$port = t3lib_div::intInRange($conf['solrPort'], 1, 65535, 8180);
// Append core name to path.
$path = trim($conf['solrPath'], '/').'/'.$core;
// Instantiate Apache_Solr_Service class.
$this->service = t3lib_div::makeInstance('Apache_Solr_Service', $host, $port, $path);
// Check if connection is established.
if ($this->service->ping() !== FALSE) {
// Do not collapse single value arrays.
$this->service->setCollapseSingleValueArrays = FALSE;
// Set core name.
$this->core = $core;
// Instantiation successful!
$this->ready = TRUE;
}
}
}
/* No xclasses for static classes!
/* No xclasses allowed for singleton classes!
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_solr.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_solr.php']);
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/dlf/common/class.tx_dlf_solr.php']);
}
*/

15
dlf/common/locallang.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3locallang>
<meta type="array">
<type>module</type>
<description>Common language labels for extension 'dlf'</description>
</meta>
<data type="array">
<languageKey index="default" type="array">
<label index="demo">demo</label>
</languageKey>
<languageKey index="de" type="array">
<label index="demo">demo</label>
</languageKey>
</data>
</T3locallang>

4
dlf/ext_tables.php

@ -197,7 +197,7 @@ $TCA['tx_dlf_libraries'] = array (
);
// Register static typoscript.
t3lib_extMgm::addStaticFile($_EXTKEY, 'typoscript/', 'DLF (Base Configuration)');
t3lib_extMgm::addStaticFile($_EXTKEY, 'typoscript/', 'Basic Configuration');
// Register plugins.
t3lib_div::loadTCA('tt_content');
@ -272,6 +272,8 @@ $TCA['tt_content']['types']['list']['subtypes_addlist'][$_EXTKEY.'_search'] = 'p
t3lib_extMgm::addPlugin(array('LLL:EXT:dlf/locallang.xml:tt_content.dlf_search', $_EXTKEY.'_search'), 'list_type');
t3lib_extMgm::addStaticFile($_EXTKEY, 'plugins/search/', 'Search Facets');
t3lib_extMgm::addPiFlexFormValue($_EXTKEY.'_search', 'FILE:EXT:'.$_EXTKEY.'/plugins/search/flexform.xml');
// Plugin "statistics".

55
dlf/hooks/class.tx_dlf_tceforms.php

@ -92,6 +92,61 @@ class tx_dlf_tceforms {
}
/**
* Helper to get flexform's items array for plugin "tx_dlf_search"
*
* @access public
*
* @param array &$params: An array with parameters
* @param t3lib_TCEforms &$pObj: The parent object
*
* @return void
*/
public function itemsProcFunc_facetsList(&$params, &$pObj) {
if ($params['row']['pi_flexform']) {
$pi_flexform = t3lib_div::xml2array($params['row']['pi_flexform']);
$pages = $pi_flexform['data']['sDEF']['lDEF']['pages']['vDEF'];
// There is a strange behavior where the uid from the flexform is prepended by the table's name and appended by its title.
// i.e. instead of "18" it reads "pages_18|Title"
if (!t3lib_div::testInt($pages)) {
$_parts = explode('|', $pages);
$pages = array_pop(explode('_', $_parts[0]));
}
if ($pages > 0) {
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'label,index_name',
'tx_dlf_metadata',
'is_facet=1 AND pid='.intval($pages).' AND (sys_language_uid IN (-1,0) OR l18n_parent=0)'.tx_dlf_helper::whereClause('tx_dlf_metadata'),
'',
'sorting',
''
);
if ($GLOBALS['TYPO3_DB']->sql_num_rows($result) > 0) {
while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_row($result)) {
$params['items'][] = $resArray;
}
}
}
}
}
/**
* Helper to get flexform's items array for plugin "tx_dlf_oai"
*

10
dlf/hooks/class.tx_dlf_tcemain.php

@ -263,12 +263,12 @@ class tx_dlf_tcemain {
if ($fieldArray['hidden']) {
// Establish Solr connection.
if ($solr = tx_dlf_solr::solrConnect($core)) {
if ($solr = tx_dlf_solr::getInstance($core)) {
// Delete Solr document.
$solr->deleteByQuery('uid:'.$id);
$solr->service->deleteByQuery('uid:'.$id);
$solr->commit();
$solr->service->commit();
}
@ -345,9 +345,9 @@ class tx_dlf_tcemain {
if ($solr = tx_dlf_solr::solrConnect($core)) {
// Delete Solr document.
$solr->deleteByQuery('uid:'.$id);
$solr->service->deleteByQuery('uid:'.$id);
$solr->commit();
$solr->service->commit();
if ($command == 'delete') {

3
dlf/plugins/collection/class.tx_dlf_collection.php

@ -258,7 +258,7 @@ class tx_dlf_collection extends tx_dlf_plugin {
// Get all documents in collection.
$result = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
'tx_dlf_collections.label AS collLabel,tx_dlf_collections.description AS collDesc,tx_dlf_documents.uid AS uid,tx_dlf_documents.metadata AS metadata,tx_dlf_documents.metadata_sorting AS metadata_sorting,tx_dlf_documents.volume_sorting AS volume_sorting,tx_dlf_documents.partof AS partof',
'tx_dlf_collections.index_name AS index_name,tx_dlf_collections.label AS collLabel,tx_dlf_collections.description AS collDesc,tx_dlf_documents.uid AS uid,tx_dlf_documents.metadata AS metadata,tx_dlf_documents.metadata_sorting AS metadata_sorting,tx_dlf_documents.volume_sorting AS volume_sorting,tx_dlf_documents.partof AS partof',
'tx_dlf_documents',
'tx_dlf_relations',
'tx_dlf_collections',
@ -283,6 +283,7 @@ class tx_dlf_collection extends tx_dlf_plugin {
'options' => array (
'source' => 'collection',
'select' => $id,
'filter' => array ('collection_faceting:"'.$resArray['index_name'].'"'),
'order' => 'title'
)
);

412
dlf/plugins/search/class.tx_dlf_search.php

@ -1,6 +1,6 @@
<?php
/***************************************************************
* Copyright notice
* Copyright notice
*
* (c) 2011 Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* All rights reserved
@ -79,14 +79,12 @@ class tx_dlf_search extends tx_dlf_plugin {
*
* @access protected
*
* @param integer $core: UID of the core
*
* @return string HTML input fields with encrypted core name and hash
*/
protected function addEncryptedCoreName($core) {
protected function addEncryptedCoreName() {
// Get core name.
$name = tx_dlf_helper::getIndexName($core, 'tx_dlf_solrcores');
$name = tx_dlf_helper::getIndexName($this->conf['solrcore'], 'tx_dlf_solrcores');
// Encrypt core name.
if (!empty($name)) {
@ -108,6 +106,129 @@ class tx_dlf_search extends tx_dlf_plugin {
}
/**
* Adds the facets menu to the search form
*
* @access protected
*
* @return string HTML output of facets menu
*/
protected function addFacetsMenu() {
// Check for typoscript configuration to prevent fatal error.
if (empty($this->conf['facetsConf.'])) {
if (TYPO3_DLOG) {
t3lib_div::devLog('[tx_dlf_search->addFacetsMenu()] Incomplete plugin configuration', $this->extKey, SYSLOG_SEVERITY_WARNING);
}
return '';
}
// Quit without doing anything if no facets are selected.
if (empty($this->conf['facets'])) {
return '';
}
// Get facets from plugin configuration.
$facets = array ();
foreach (t3lib_div::trimExplode(',', $this->conf['facets'], TRUE) as $facet) {
$facets[$facet] = tx_dlf_helper::translate($facet, 'tx_dlf_metadata', $this->conf['pages']);
}
// Render facets menu.
$TSconfig = array ();
$TSconfig['special'] = 'userfunction';
$TSconfig['special.']['userFunc'] = 'tx_dlf_search->makeFacetsMenuArray';
$TSconfig['special.']['facets'] = $facets;
$TSconfig = t3lib_div::array_merge_recursive_overrule($this->conf['facetsConf.'], $TSconfig);
return $this->cObj->HMENU($TSconfig);
}
/**
* Creates an array for a HMENU entry of a facet value.
*
* @param string $field: The facet's index_name
* @param string $value: The facet's value
* @param integer $count: Number of hits for this facet
* @param array $search: The parameters of the current search query
* @param string &$state: The state of the parent item
*
* @return array The array for the facet's menu entry
*/
protected function getFacetsMenuEntry($field, $value, $count, $search, &$state) {
$entryArray = array();
// Translate value.
if ($field == 'owner_faceting') {
// Translate name of holding library.
$entryArray['title'] = tx_dlf_helper::translate($value, 'tx_dlf_libraries', $this->conf['pages']);
} elseif ($field == 'type_faceting') {
// Translate document type.
$entryArray['title'] = tx_dlf_helper::translate($value, 'tx_dlf_structures', $this->conf['pages']);
} elseif ($field == 'language_faceting') {
// Translate ISO 639 language code.