Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Open
LU Search
Commits
1c31b146
Commit
1c31b146
authored
Dec 17, 2020
by
Dainis Abols
Browse files
Initial public version
1.0.0
parents
Pipeline
#153
failed with stages
in 0 seconds
Changes
37
Pipelines
2
Show whitespace changes
Inline
Side-by-side
.gitignore
0 → 100644
View file @
1c31b146
.idea/
Classes/Controller/SearchController.php
0 → 100644
View file @
1c31b146
<?php
namespace
Lu\LuSearch\Controller
;
use
HDNET\Calendarize\ViewHelpers\Link\AbstractLinkViewHelper
;
use
Lu\LuApi\DataSources\SolrSearch
;
use
Lu\LuSearch\Helpers\DataHelper
;
use
TYPO3\CMS\Core\Context\Context
;
use
TYPO3\CMS\Core\Database\ConnectionPool
;
use
TYPO3\CMS\Core\Exception\SiteNotFoundException
;
use
TYPO3\CMS\Core\Page\PageRenderer
;
use
TYPO3\CMS\Core\Resource\File
;
use
TYPO3\CMS\Core\Resource\FileReference
;
use
TYPO3\CMS\Core\Resource\FileRepository
;
use
TYPO3\CMS\Core\Resource\ResourceStorage
;
use
TYPO3\CMS\Core\Site\SiteFinder
;
use
TYPO3\CMS\Core\Utility\GeneralUtility
;
use
TYPO3\CMS\Extbase\Mvc\Controller\ActionController
;
use
TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
;
use
TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
;
/**
* Class SearchController
*
* @author Dainis Abols <dainis.abols@lu.lv>
* @owner University of Latvia
* @version 1.0.0
* @since 02.09.2020
*
* @package Lu\LuSearch\Controller
*/
class
SearchController
extends
ActionController
{
/**
* Frontend-Session
*
* @var \Lu\LuSearch\Domain\Session\FrontendSessionHandler
*/
protected
$frontendSession
;
/**
* Frontend- or Backend-Session
* The type of session is set automatically in initializeAction().
*
* @var \Lu\LuSearch\Domain\Session\SessionHandler
*/
protected
$session
;
/**
* Main language array
*
* @var array
*/
private
$language
;
/**
* Uri Builder object
*
* @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
*/
private
$uri_builder
;
/**
* Selected page number
*
* @var int
*/
private
$currentPage
;
/**
* Filter array
*
* @var array
*/
private
$filter
;
/**
* Define pagination limit
*
* @var int
*/
private
$limit
=
25
;
/**
* Parent IDs
*
* @var array
*/
private
$parentIDs
=
[];
/**
* MusicController constructor.
*
* Assign page ID
*/
public
function
__construct
()
{
parent
::
__construct
();
// Set language data
$languageAspect
=
GeneralUtility
::
makeInstance
(
Context
::
class
)
->
getAspect
(
'language'
);
$this
->
language
=
[
'id'
=>
$languageAspect
->
getId
(),
'iso'
=>
$GLOBALS
[
'TSFE'
]
->
sys_language_isocode
,
'code'
=>
$GLOBALS
[
'TSFE'
]
->
sys_language_isocode
==
'lv'
?
'lat'
:
'eng'
,
];
// Set uri builder
$this
->
uri_builder
=
GeneralUtility
::
makeInstance
(
UriBuilder
::
class
)
->
setCreateAbsoluteUri
(
true
)
->
reset
();
}
/**
* List Action
* Build search field and displays results.
*
* /search?query=<query-string>
*
* @return void
*/
public
function
listAction
()
{
// Pre-define variables
$data
=
[];
$request
=
$this
->
request
->
getArguments
();
// Find pagetree if limit set
if
(
$this
->
settings
[
'limitToDomain'
])
{
// Get page record for tree starting point
$this
->
parentIDs
=
$this
->
buildTree
(
$this
->
getParentSite
(
$GLOBALS
[
'TSFE'
]
->
id
)
->
getRootPageId
());
}
// Get current page @FIXME There has to be a better way!
$this
->
currentPage
=
!
empty
(
$request
[
'@widget_0'
][
'currentPage'
])
?
(
int
)
$request
[
'@widget_0'
][
'currentPage'
]
:
1
;
// Fetch data from SOLR
$cleanQuery
=
strip_tags
(
$request
[
'query'
]);
$cleanQuery
=
strlen
(
$cleanQuery
)
>=
3
?
$cleanQuery
:
''
;
if
(
$cleanQuery
)
{
$data
=
$this
->
listResults
(
$cleanQuery
);
}
// Add highlighting
$singleTranslate
=
false
;
if
(
$data
[
'numFound'
]
>
0
||
$data
[
'response'
][
'numFound'
]
>
0
)
{
$numRows
=
$data
[
'numFound'
]
>
0
?
$data
[
'numFound'
]
:
$data
[
'response'
][
'numFound'
];
$results
=
$this
->
buildResult
(
$data
,
$cleanQuery
);
$this
->
view
->
assign
(
'results'
,
$results
);
// Set what type of text to use (Latvian number formats only)
if
(
substr
(
$numRows
,
-
1
)
==
1
&&
substr
(
$numRows
,
-
2
)
!=
11
)
{
$singleTranslate
=
true
;
}
// Add paginate object
$data
[
'paginate'
]
=
[];
for
(
$k
=
0
;
$k
<
$numRows
;
$k
++
)
{
$data
[
'paginate'
][]
=
$k
;
}
}
// Add custom JS
$this
->
renderJs
();
// Assign variables
$this
->
view
->
assign
(
'query'
,
$cleanQuery
);
$this
->
view
->
assign
(
'currentPage'
,
$this
->
currentPage
);
$this
->
view
->
assign
(
'singleTranslate'
,
$singleTranslate
);
$this
->
view
->
assign
(
'pageId'
,
$GLOBALS
[
'TSFE'
]
->
id
);
$this
->
view
->
assign
(
'language'
,
$this
->
language
);
$this
->
view
->
assign
(
'newsPageId'
,
$this
->
language
);
// Assign final data element to view
$this
->
view
->
assign
(
'data'
,
$data
);
}
/**
* Searches in
* (title:<query> OR author:<query>OR body:<query>)
*
* Adds type, based on settings
* (type:news OR ...)
*
* @param $query
*/
private
function
listResults
(
$query
)
{
// Pre-set models and values
$dataHelper
=
new
DataHelper
();
// Search in SOLR
$solr
=
new
SolrSearch
();
$solr
->
setLimit
(
$this
->
limit
);
$solr
->
setOffset
((
$this
->
currentPage
-
1
)
*
$this
->
limit
);
$solr
->
setHighlight
(
"<span class=
\"
searchResults__searchTerm
\"
>|</span>"
);
// Set filters
$filters
=
[
'title:"'
.
$query
.
'"'
,
'author:"'
.
$query
.
'"'
,
'body:"'
.
$query
.
'"'
];
$solr
->
setFilters
(
$filters
);
// Set parent IDs
$solr
->
setParentIDs
(
$this
->
parentIDs
);
// Set types
$types
=
[];
if
(
$this
->
settings
[
'searchNews'
])
{
$types
[]
=
'type:news'
;
}
if
(
$this
->
settings
[
'searchContent'
])
{
$types
[]
=
'type:content'
;
}
// if ($this->settings['searchContacts']) {
// $types[] = 'type:contact';
// }
if
(
$this
->
settings
[
'searchEvents'
])
{
$types
[]
=
'type:event'
;
}
if
(
$this
->
settings
[
'searchPages'
])
{
$types
[]
=
'type:page'
;
}
$solr
->
setTypes
(
$types
);
// Fetch data
$jsonData
=
$solr
->
fetch
(
true
);
$result
=
$dataHelper
->
APIDecode
(
$jsonData
);
return
$result
;
}
/**
* Build results array for use in multiple paginations
*
* @param $data
* @param $cleanQuery
*
* @return array
*/
private
function
buildResult
(
$data
,
$cleanQuery
)
{
// Fetch variables for easier overview
$response
=
$data
[
'response'
]
??
$data
;
$highlight
=
$data
[
'highlighting'
]
??
[];
// Build response array
$result
=
[
'recordsTotal'
=>
"<span class=
\"
searchResults__queryCountNum
\"
>"
.
$response
[
'numFound'
]
.
"</span>"
,
'recordsTotal_clean'
=>
$response
[
'numFound'
],
'recordsFiltered'
=>
count
(
$response
[
'docs'
]),
'pageTotal'
=>
ceil
(
$response
[
'numFound'
]
/
$this
->
limit
),
'pageCurrent'
=>
$this
->
currentPage
,
'data'
=>
[],
];
// Add data info
foreach
(
$response
[
'docs'
]
as
$item
)
{
// Set site data
$siteData
=
$this
->
getParentSite
(
$item
[
'parent_id'
]);
// Fetch data
$uid
=
str_replace
(
$item
[
'type'
]
.
'-'
,
''
,
$item
[
'id'
]);
$title
=
$highlight
[
$item
[
'id'
]][
'title'
][
0
]
??
$this
->
clearAndHighlight
(
$item
[
'title'
],
$cleanQuery
,
false
);
$siteUrl
=
!
empty
(
$siteData
)
?
'https://'
.
$siteData
->
getBase
()
->
getHost
()
:
''
;
if
(
empty
(
$title
))
{
// If title is not still set, try to get parent page title
$title
=
$this
->
getPageTitle
(
$item
[
'parent_id'
]);
}
$image
=
!
empty
(
$siteData
)
?
$this
->
addNewsImage
((
int
)
$uid
)
:
''
;
if
(
empty
(
$image
))
{
// If still empty, check page images recursivly
$image
=
$this
->
getParentImage
(
$item
[
'parent_id'
]);
}
// Add values to array
$result
[
'data'
][]
=
[
'uid'
=>
$uid
,
'parent_id'
=>
$item
[
'parent_id'
],
'type'
=>
$item
[
'type'
],
'date_published'
=>
$item
[
'date_published'
],
'author'
=>
$highlight
[
$item
[
'id'
]][
'author'
][
0
]
??
$this
->
clearAndHighlight
(
$item
[
'author'
],
$cleanQuery
),
'title'
=>
$title
,
'body'
=>
$highlight
[
$item
[
'id'
]][
'body'
][
0
]
??
$this
->
clearAndHighlight
(
$item
[
'body'
],
$cleanQuery
),
'siteName'
=>
!
empty
(
$siteData
)
?
$this
->
getSiteName
(
$siteData
->
getRootPageId
())
:
''
,
'siteUrl'
=>
$siteUrl
,
'newsId'
=>
!
empty
(
$siteData
)
?
$this
->
addNewsIds
(
$siteData
->
getRootPageId
())
:
''
,
'calendarId'
=>
!
empty
(
$siteData
)
?
$this
->
addCalendarId
(
$uid
)
:
''
,
'imgUrl'
=>
$image
,
];
}
// Return result
return
$result
;
}
/**
* Removed all tags and newlines and adds highlight to search words
*
* @param $string
* @param $cleanQuery
* @param bool $crop
*
* @return string
*/
private
function
clearAndHighlight
(
$string
,
$cleanQuery
,
$crop
=
true
)
{
// Clear
$string
=
trim
(
preg_replace
(
'/\s\s+/'
,
' '
,
strip_tags
(
$string
)));
if
(
$crop
)
{
$string
=
substr
(
$string
,
0
,
strrpos
(
substr
(
$string
,
0
,
1000
),
' '
));
}
// Highlight
$string
=
preg_replace
(
"/("
.
$cleanQuery
.
")/i"
,
"<span class=
\"
searchResults__searchTerm
\"
>$1</span>"
,
$string
);
// Return
return
$string
;
}
/**
* Adds our own custom js
*/
private
function
renderJs
()
{
$pageRenderer
=
GeneralUtility
::
makeInstance
(
PageRenderer
::
class
);
// Add jQuery UI
$pageRenderer
->
addCssFile
(
"//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"
);
$pageRenderer
->
addJsFooterFile
(
"//code.jquery.com/jquery-1.12.4.js"
);
$pageRenderer
->
addJsFooterFile
(
"//code.jquery.com/ui/1.12.1/jquery-ui.js"
);
// Add own JS
$pageRenderer
->
addJsFooterFile
(
"/typo3conf/ext/lu_search/Resources/Public/JavaScript/scripts.js"
);
}
/**
* Builds tree
*
* @param $startingPoint
* @param int $depth
*/
public
function
buildTree
(
$startingPoint
,
$depth
=
100
)
{
// Get page record for tree starting point
$pageRecord
=
\
TYPO3\CMS\Backend\Utility\BackendUtility
::
getRecord
(
'pages'
,
$startingPoint
);
// Create and initialize the tree object
/** @var $tree \TYPO3\CMS\Backend\Tree\View\PageTreeView */
$tree
=
\
TYPO3\CMS\Core\Utility\GeneralUtility
::
makeInstance
(
\
TYPO3\CMS\Backend\Tree\View\PageTreeView
::
class
);
// Creating the icon for the current page and add it to the tree
$tree
->
tree
[]
=
[
'row'
=>
$pageRecord
,
'HTML'
=>
''
,
];
// Create the page tree, from the starting point, 2 levels deep
$tree
->
getTree
(
$startingPoint
,
$depth
,
''
);
// Fetch only uids and retrn data
$treeIds
=
[];
foreach
(
$tree
->
tree
as
$item
)
{
$treeIds
[]
=
$item
[
'row'
][
'uid'
];
}
// Return
return
$treeIds
;
}
/**
* Recursive method for finding site objects
*
* @return \array|\TYPO3\CMS\Core\Site\Entity\Site
*/
private
function
getParentSite
(
$current_id
=
0
)
{
// Get current ID if parent ID = 0
if
(
!
$current_id
)
{
$current_id
=
$GLOBALS
[
'TSFE'
]
->
id
;
}
$parent_id
=
$this
->
getParentId
(
$current_id
);
// Check if parent exists
if
(
$parent_id
<
1
)
{
return
[];
}
// Try to find site info
try
{
$sites
=
GeneralUtility
::
makeInstance
(
SiteFinder
::
class
)
->
getSiteByPageId
(
$parent_id
);
}
catch
(
SiteNotFoundException
$e
)
{
return
[];
}
// Go up, if no site founf
if
(
empty
(
$sites
))
{
return
$this
->
getParentSite
(
$parent_id
);
}
// Return site data
return
$sites
;
}
/**
* Fetch current page parent id
*
* @param $current_id
*
* @return int
*/
private
function
getParentId
(
$current_id
):
int
{
$queryBuilder
=
GeneralUtility
::
makeInstance
(
ConnectionPool
::
class
)
->
getConnectionForTable
(
'pages'
)
->
createQueryBuilder
();
return
(
int
)
$queryBuilder
->
select
(
'pid'
)
->
from
(
'pages'
)
->
where
(
$queryBuilder
->
expr
()
->
eq
(
'uid'
,
$current_id
)
)
->
execute
()
->
fetchColumn
(
0
);
}
/**
* Fetch page title
*
* @param $uid
*
* @return string
*/
private
function
getSiteName
(
int
$uid
):
string
{
$queryBuilder
=
GeneralUtility
::
makeInstance
(
ConnectionPool
::
class
)
->
getConnectionForTable
(
'pages'
)
->
createQueryBuilder
();
return
(
string
)
$queryBuilder
->
select
(
'title'
)
->
from
(
'pages'
)
->
where
(
$queryBuilder
->
expr
()
->
eq
(
'uid'
,
$uid
)
)
->
execute
()
->
fetchColumn
(
0
);
}
/**
* Get constants entry by root page id
*
* @param $rootPageId
*/
private
function
addNewsIds
(
$rootPageId
)
{
// SELECT constants FROM `sys_template` WHERE pid = 468 AND deleted = 0
$queryBuilder
=
GeneralUtility
::
makeInstance
(
ConnectionPool
::
class
)
->
getConnectionForTable
(
'sys_template'
)
->
createQueryBuilder
();
$statement
=
$queryBuilder
->
select
(
'constants'
)
->
from
(
'sys_template'
)
->
where
(
$queryBuilder
->
expr
()
->
eq
(
'pid'
,
$rootPageId
),
$queryBuilder
->
expr
()
->
eq
(
'deleted'
,
0
)
)
->
execute
();
// Try finding news ID entries
while
(
$row
=
$statement
->
fetch
())
{
foreach
(
preg_split
(
"/((
\r
?
\n
)|(
\r\n
?))/"
,
$row
[
'constants'
])
as
$line
)
{
if
(
!
empty
(
$line
))
{
$contsOptions
=
explode
(
"="
,
$line
);
if
(
trim
(
$contsOptions
[
0
])
==
'news.defaultDetailPid'
)
{
return
(
int
)
$contsOptions
[
1
];
}
}
}
}
// Return results
return
0
;
}
/**
* Fetch news image, if available
*
* @param $uid
*
* @return string
*/
private
function
addNewsImage
(
$uid
)
{
// Fetch image from storage
$fileRepository
=
GeneralUtility
::
makeInstance
(
FileRepository
::
class
);
$fileObjects
=
$fileRepository
->
findByRelation
(
'tx_news_domain_model_news'
,
'fal_media'
,
$uid
);
// Build image
if
(
!
empty
(
$fileObjects
))
{
foreach
(
$fileObjects
as
$fileRef
)
{
// Check if allowed list view
if
(
$fileRef
->
getProperties
()[
'showinpreview'
])
{
return
$this
->
getPublicPath
(
$fileRef
);
}
}
}
// Return empty
return
''
;
}
/**
* Fetch page title by id
*
* @param $page_id
*
* @return string
*/
private
function
getPageTitle
(
$page_id
)
{
$queryBuilder
=
GeneralUtility
::
makeInstance
(
ConnectionPool
::
class
)
->
getConnectionForTable
(
'page'
)
->
createQueryBuilder
();
$statement
=
$queryBuilder
->
select
(
'title'
,
'slug'
)
->
from
(
'pages'
)
->
where
(
$queryBuilder
->
expr
()
->
eq
(
'uid'
,
$page_id
),
)
->
execute
();
// Get info
$result
=
$statement
->
fetch
();
return
$result
[
'title'
];
}
/**
* Return public path
*
* @param $fileRef
*
* @return string
*/
private
function
getPublicPath
(
$fileRef
)
{
return
$fileRef
->
getStorage
()
->
getConfiguration
()[
'basePath'
]
.
$fileRef
->
getIdentifier
();
}
/**
* Recursivly retrieve title image
*
* @param $uid
*
* @return string
*/
private
function
getParentImage
(
$uid
)
{
// Fetch image from storage
$fileRepository
=
GeneralUtility
::
makeInstance
(
FileRepository
::
class
);
$fileObjects
=
$fileRepository
->
findByRelation
(
'pages'
,
'media'
,
$uid
);
// Fetch level up
if
(
empty
(
$fileObjects
))
{
$queryBuilder
=
GeneralUtility
::
makeInstance
(
ConnectionPool
::
class
)
->
getConnectionForTable
(
'page'
)
->
createQueryBuilder
();
$statement
=
$queryBuilder
->
select
(
'pid'
)
->
from
(
'pages'
)
->
where
(
$queryBuilder
->
expr
()
->
eq
(
'uid'
,
$uid
),
)
->
execute
();
$result
=
$statement
->
fetch
();
if
(
empty
(
$result
))
{
return
''
;
}
// Call level up
return
$this
->
getParentImage
(
$result
[
'pid'
]);
}
// Build first element image
return
$this
->
getPublicPath
(
$fileObjects
[
0
]);
}
private
function
addCalendarId
(
$uid
)
{
return
55699
;
}
}
\ No newline at end of file
Classes/Domain/Session/BackendSessionHandler.php
0 → 100644
View file @
1c31b146
<?php
namespace
Lu\LuSearch\Domain\Session
;
/**
* Class BackendSessionHandler
*
* @author Dainis Abols <dainis.abols@lu.lv>
* @owner University of Latvia
* @version 1.0.0
* @since 02.09.2020
*
* @package Lu\LuSearch\Domain\Session
*/
class
BackendSessionHandler
extends
SessionHandler
{
/**
* @var string
*/
protected
$mode
=
"BE"
;
}
\ No newline at end of file
Classes/Domain/Session/FrontendSessionHandler.php
0 → 100644
View file @
1c31b146
<?php
namespace
Lu\LuSearch\Domain\Session
;
/**
* Class BackendSessionHandler