implement basic unified search for courses

Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
This commit is contained in:
Julien Veyssier 2020-09-07 17:34:43 +02:00
parent 1c0226ba05
commit f975d70b51
No known key found for this signature in database
GPG key ID: 4141FEE162030638
6 changed files with 228 additions and 2 deletions

View file

@ -19,6 +19,7 @@ use OCP\AppFramework\Bootstrap\IBootstrap;
use OCA\Moodle\Controller\PageController;
use OCA\Moodle\Dashboard\MoodleWidget;
use OCA\Moodle\Search\MoodleSearchProvider;
/**
* Class Application
@ -42,6 +43,7 @@ class Application extends App implements IBootstrap {
public function register(IRegistrationContext $context): void {
$context->registerDashboardWidget(MoodleWidget::class);
$context->registerSearchProvider(MoodleSearchProvider::class);
}
public function boot(IBootContext $context): void {

View file

@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Julien Veyssier
*
* @author Julien Veyssier <eneiluj@posteo.net>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Moodle\Search;
use OCA\Moodle\Service\MoodleAPIService;
use OCA\Moodle\AppInfo\Application;
use OCP\App\IAppManager;
use OCP\IL10N;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
class MoodleSearchProvider implements IProvider {
/** @var IAppManager */
private $appManager;
/** @var IL10N */
private $l10n;
/** @var IURLGenerator */
private $urlGenerator;
/**
* CospendSearchProvider constructor.
*
* @param IAppManager $appManager
* @param IL10N $l10n
* @param IURLGenerator $urlGenerator
* @param MoodleAPIService $service
*/
public function __construct(IAppManager $appManager,
IL10N $l10n,
IConfig $config,
IURLGenerator $urlGenerator,
MoodleAPIService $service) {
$this->appManager = $appManager;
$this->l10n = $l10n;
$this->config = $config;
$this->urlGenerator = $urlGenerator;
$this->service = $service;
}
/**
* @inheritDoc
*/
public function getId(): string {
return 'moodle-search';
}
/**
* @inheritDoc
*/
public function getName(): string {
return $this->l10n->t('Moodle');
}
/**
* @inheritDoc
*/
public function getOrder(string $route, array $routeParameters): int {
if (strpos($route, Application::APP_ID . '.') === 0) {
// Active app, prefer Moodle results
return -1;
}
return 20;
}
/**
* @inheritDoc
*/
public function search(IUser $user, ISearchQuery $query): SearchResult {
if (!$this->appManager->isEnabledForUser(Application::APP_ID, $user)) {
return SearchResult::complete($this->getName(), []);
}
$limit = $query->getLimit();
$term = $query->getTerm();
$offset = $query->getCursor();
$theme = $this->config->getUserValue($user->getUID(), 'accessibility', 'theme', '');
$thumbnailUrl = ($theme === 'dark') ?
$this->urlGenerator->imagePath(Application::APP_ID, 'app.svg') :
$this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg');
$resultBills = [];
$moodleUrl = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'url', '');
$accessToken = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'token', '');
$searchEnabled = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'search_enabled', '0') === '1';
if ($accessToken === '' || !$searchEnabled) {
return SearchResult::paginated($this->getName(), [], 0);
}
$searchResults = $this->service->search($moodleUrl, $accessToken, $term);
$formattedResults = \array_map(function (array $entry) use ($thumbnailUrl, $moodleUrl): MoodleSearchResultEntry {
return new MoodleSearchResultEntry(
$thumbnailUrl,
$this->getMainText($entry),
$this->getSubline($entry),
$this->getLinkToMoodle($entry, $moodleUrl),
'',
true
);
}, $searchResults);
return SearchResult::paginated(
$this->getName(),
$formattedResults,
$query->getCursor() + count($formattedResults)
);
}
/**
* @return string
*/
protected function getMainText(array $entry): string {
return $entry['displayname'];
}
/**
* @return string
*/
protected function getSubline(array $entry): string {
return $this->l10n->t('Moodle course');
}
/**
* @return string
*/
protected function getLinkToMoodle(array $entry, string $url): string {
return $url . '/course/view.php?id=' . $entry['id'];
}
}

View file

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Julien Veyssier
*
* @author Julien Veyssier <eneiluj@posteo.net>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Moodle\Search;
use OCP\Search\SearchResultEntry;
class MoodleSearchResultEntry extends SearchResultEntry {
}

View file

@ -103,6 +103,18 @@ class MoodleAPIService {
];
}
public function search(string $url, string $accessToken, string $query): array {
$params = [
'wstoken' => $accessToken,
'wsfunction' => 'core_course_search_courses',
'moodlewsrestformat' => 'json',
'criterianame' => 'search',
'criteriavalue' => $query,
];
$searchResult = $this->request($url, 'webservice/rest/server.php', $params);
return $searchResult['courses'];
}
public function getMoodleAvatar($url) {
$rawResult = $this->client->get($url)->getBody();
$success = preg_match('/<svg.*/', $rawResult, $matches);
@ -113,7 +125,7 @@ class MoodleAPIService {
} else {
$result = $rawResult;
}
error_log('RESult['.$success.'] '.$result);
//error_log('RESult['.$success.'] '.$result);
return $result;
}

View file

@ -43,10 +43,12 @@ class Personal implements ISettings {
public function getForm() {
$token = $this->config->getUserValue($this->userId, Application::APP_ID, 'token', '');
$url = $this->config->getUserValue($this->userId, Application::APP_ID, 'url', '');
$searchEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_enabled', '0');
$userConfig = [
'token' => $token,
'url' => $url
'url' => $url,
'search_enabled' => ($searchEnabled === '1')
];
$this->initialStateService->provideInitialState($this->appName, 'user-config', $userConfig);
return new TemplateResponse(Application::APP_ID, 'personalSettings');

View file

@ -52,6 +52,15 @@
:placeholder="t('integration_moodle', 'Authenticate with OAuth')"
@input="onInput">
</div>
<div id="moodle-search-block">
<input
id="search-moodle"
type="checkbox"
class="checkbox"
:checked="state.search_enabled"
@input="onSearchChange">
<label for="search-moodle">{{ t('integration_moodle', 'Enable unified search for courses.') }}</label>
</div>
</div>
</template>
@ -85,6 +94,10 @@ export default {
},
methods: {
onSearchChange(e) {
this.state.search_enabled = e.target.checked
this.saveOptions()
},
onInput() {
const that = this
delay(function() {
@ -103,6 +116,7 @@ export default {
values: {
token: this.state.token,
url: this.state.url,
search_enabled: this.state.search_enabled ? '1' : '0',
},
}
const url = generateUrl('/apps/integration_moodle/config')
@ -150,6 +164,10 @@ export default {
</script>
<style scoped lang="scss">
#moodle-search-block {
margin-left: 30px;
margin-top: 30px;
}
.moodle-grid-form label {
line-height: 38px;
}