diff --git a/app/controllers/oer/market.php b/app/controllers/oer/market.php index f9d203c0dbe1cf685464e11ece10ce98c6229bb1..23782a44e2a995f1266e0615b369331653847ef7 100755 --- a/app/controllers/oer/market.php +++ b/app/controllers/oer/market.php @@ -62,6 +62,12 @@ class Oer_MarketController extends StudipController $tags = $this->tag_history = Request::getArray("tags"); $this->without_tags = []; $tag_to_search_for = array_pop($tags); + + OERMaterial::fetchRemoteSearch( + null, + $tag_to_search_for + ); + foreach (OERTag::findBest($tag_matrix_entries_number, true) as $related_tag) { if ($related_tag['tag_hash'] !== $this->tag_history[0]) { $this->without_tags[] = $related_tag['tag_hash']; @@ -203,7 +209,12 @@ class Oer_MarketController extends StudipController if (Navigation::hasItem("/oer/market")) { Navigation::activateItem("/oer/market"); } - $this->material = new OERMaterial($material_id); + $this->material = OERMaterial::find($material_id); + if (!$this->material) { + PageLayout::postError(_('Lernmaterial existiert nicht mehr.')); + $this->redirect('oer/market'); + return; + } //OpenGraph tags: PageLayout::addHeadElement("meta", ['og:title' => $this->material['name']]); diff --git a/app/views/oer/embed/standard.php b/app/views/oer/embed/standard.php index 3060b716faa456f96865a84ac96f43b7beed168e..4374c376fab9b0a0aa7902a1f33cb5fee9bf3df1 100644 --- a/app/views/oer/embed/standard.php +++ b/app/views/oer/embed/standard.php @@ -1,4 +1,8 @@ -<a href="<?= htmlReady($url) ?>"> - <?= Icon::create("service", Icon::ROLE_CLICKABLE)->asImg(16, ['class' => "text-bottom"]) ?> +<? if ($url) : ?> + <a href="<?= htmlReady($url) ?>"> +<? else : ?> + <a href="<?= htmlReady($source_url) ?>" target="_blank"> +<? endif ?> + <?= Icon::create('oer-campus')->asImg(['class' => 'text-bottom']) ?> <?= htmlReady($material['name']) ?> </a> diff --git a/app/views/oer/market/_searchform.php b/app/views/oer/market/_searchform.php index c731ac8d01bfc60470f4f726617ae310895114ed..995eb9cbec29cd035da81166ce330cfa77cb3f82 100644 --- a/app/views/oer/market/_searchform.php +++ b/app/views/oer/market/_searchform.php @@ -169,7 +169,7 @@ role="clickable" size="20" class="text-bottom"></studip-icon> - {{ result.name }} + {{ shortenName(result.name) }} </h1> </header> <div class="image" :style="'background-image: url(' + result.logo_url + ');' + (!result.front_image_content_type ? ' background-size: 60% auto;': '')"></div> diff --git a/app/views/oer/market/details.php b/app/views/oer/market/details.php index 2c84011c11b8c9dbc31affdb06247658d220c48e..a42114f36c1bbd18344fed52270f49b4670c2202 100755 --- a/app/views/oer/market/details.php +++ b/app/views/oer/market/details.php @@ -1,6 +1,6 @@ <?= $contentbar ?> -<? $url = $material['host_id'] ? $material->host->url."download/".$material['foreign_material_id'] : $controller->link_for("oer/endpoints/download/".$material->getId()) ?> +<? $url = $material->getDownloadUrl() ?> <? if ($material['player_url']) : ?> <iframe src="<?= htmlReady($material['player_url']) ?>" @@ -9,7 +9,7 @@ <? elseif ($material->isVideo()) : ?> <video controls <?= $material['front_image_content_type'] ? 'poster="'.htmlReady($material->getLogoURL()).'"' : "" ?> - crossorigin="anonymous" + crossorigin="use-credentials" src="<?= htmlReady($url) ?>" class="lernmarktplatz_player"></video> <? elseif ($material->isAudio()) : ?> @@ -40,9 +40,9 @@ </ol> <? endif ?> -<? if (($url && $material['filename'] || (!$material['host_id'] && ($material->isMine() || $GLOBALS['perm']->have_perm("root"))))) : ?> +<? if (($url && $material['filename']) || $material['source_url'] || (!$material['host_id'] && ($material->isMine() || $GLOBALS['perm']->have_perm("root")))) : ?> <div class="center bordered" style="margin-top: 20px; margin-bottom: 20px;"> - <? if ($url && $material['filename']) : ?> + <? if ($url) : ?> <a class="button" href="<?= htmlReady($url) ?>" title="<?= _('Herunterladen') ?>" download="<?= htmlReady($material['filename']) ?>"> @@ -50,6 +50,14 @@ </a> <? endif ?> + <? if ($material['source_url']) : ?> + <a class="button" + target="_blank" + href="<?= htmlReady($material['source_url']) ?>"> + <?= _('Webseite') ?> + </a> + <? endif ?> + <? if ($GLOBALS['perm']->have_perm("autor")) : ?> <a class="button" href="<?= $controller->link_for( "oer/market/add_to_course/" . $material->getId()) ?>" @@ -142,170 +150,151 @@ <?= formatReady($material['description']) ?> </div> - <h2><?= _('Zum Autor') ?></h2> - <ul class="author_information clean"> - <? foreach ($material->users as $materialuser) : ?> - <li> - <? if ($materialuser['external_contact']) : ?> - <? $user = $materialuser['oeruser'] ?> - <? $image = $user['avatar_url'] ?> - <? $host = OERHost::find($user['host_id']) ?> - <div class="avatar" style="background-image: url('<?= $image ?>');"></div> - <div> - <div class="author_name"> - <a href="<?= $controller->link_for("oer/market/profile/".$user->getId()) ?>"> - <?= htmlReady($user['name']) ?> - </a> - </div> - <div class="author_host">(<?= htmlReady($host->name) ?>)</div> - <div class="description"><?= formatReady($user['data']['description']) ?></div> - </div> - <? else : ?> - <? $user = User::find($materialuser['user_id']) ?> - <? $image = Avatar::getAvatar($materialuser['user_id'])->getURL(Avatar::MEDIUM) ?> - <div class="avatar" style="background-image: url('<?= $image ?>');"></div> + <? $authors = $material->getAuthors() ?> + <? if (count($authors)) : ?> + <h2><?= _('Zum Autor') ?></h2> + <ul class="author_information clean"> + <? foreach ($material->getAuthors() as $authordata) : ?> + <li> + <div class="avatar" style="background-image: url('<?= htmlReady($authordata['avatar'] ?: Avatar::getNobody()->getURL(Avatar::MEDIUM)) ?>');"></div> <div> <div class="author_name"> - <a href="<?= URLHelper::getLink("dispatch.php/profile", ['username' => $user['username']]) ?>"> - <?= htmlReady($user ? $user->getFullName() : _('unbekannt')) ?> + <? if ($authordata['link']) : ?> + <a href="<?= htmlReady($authordata['link']) ?>"> + <? endif ?> + <?= htmlReady($authordata['name']) ?> + <? if ($authordata['link']) : ?> </a> - </div> - <div class="author_host">(<?= htmlReady(Config::get()->UNI_NAME_CLEAN) ?>)</div> - <div class="description"> - <? if ($user['oercampus_description']) : ?> - <?= htmlReady($user['oercampus_description']) ?> - <? elseif ($materialuser['user_id'] === $GLOBALS['user']->id) : ?> - <em> - <?= sprintf(_('Noch keine Beschreibung für den %s vorhanden.'), Config::get()->OER_TITLE) ?> - <a href="<?= URLHelper::getLink("dispatch.php/settings/details") ?>"> - <?= _('Jetzt eine eingeben.') ?> - </a> - </em> <? endif ?> </div> + <div class="author_host">(<?= htmlReady($authordata['hostname']) ?>)</div> + <? if ($authordata['description']) : ?> + <div class="description"><?= formatReady($authordata['description']) ?></div> + <? endif ?> </div> - <? endif ?> - </li> - <? endforeach ?> - </ul> + </li> + <? endforeach ?> + </ul> + <? endif ?> </div> </div> -<? $allowed_to_review = !$material->isMine() && $GLOBALS['perm']->have_perm("autor") ?> -<? if (!$material->isMine() || count($material->reviews)) : ?> - <article class="studip bordered"> - <div class="center"> - <? if ($material['rating'] === null) : ?> - <? if ($allowed_to_review) : ?> - <a style="opacity: 0.3;" - title="<?= $GLOBALS['perm']->have_perm("autor") ? _('Geben Sie die erste Bewertung ab.') : _('Noch keine Bewertung abgegeben.') ?>" - href="<?= $controller->link_for('oer/market/review/' . $material->getId()) ?>" data-dialog> - <? endif ?> - <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> - <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> - <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> - <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> - <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> - <? if ($allowed_to_review) : ?> - </a> - <? endif ?> - <? else : ?> - <? if ($allowed_to_review) : ?> - <a href="<?= $controller->link_for('oer/market/review/' . $material->getId()) ?>" data-dialog title="<?= sprintf(_('%s von 5 Sternen'), round($material['rating'] / 2, 1)) ?>"> - <? endif ?> - <? $material['rating'] = round($material['rating'], 1) / 2 ?> - <? $v = $material['rating'] >= 0.75 ? "" : ($material['rating'] >= 0.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> - <? $v = $material['rating'] >= 1.75 ? "" : ($material['rating'] >= 1.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> - <? $v = $material['rating'] >= 2.75 ? "" : ($material['rating'] >= 2.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> - <? $v = $material['rating'] >= 3.75 ? "" : ($material['rating'] >= 3.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> - <? $v = $material['rating'] >= 4.75 ? "" : ($material['rating'] >= 4.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> - <? if ($allowed_to_review) : ?> - </a> +<? if ($material->host && $material->host->isReviewable()) : ?> + <? $allowed_to_review = !$material->isMine() && $GLOBALS['perm']->have_perm("autor") ?> + <? if (!$material->isMine() || count($material->reviews)) : ?> + <article class="studip bordered"> + <div class="center"> + <? if ($material['rating'] === null) : ?> + <? if ($allowed_to_review) : ?> + <a style="opacity: 0.3;" + title="<?= $GLOBALS['perm']->have_perm("autor") ? _('Geben Sie die erste Bewertung ab.') : _('Noch keine Bewertung abgegeben.') ?>" + href="<?= $controller->link_for('oer/market/review/' . $material->getId()) ?>" data-dialog> + <? endif ?> + <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> + <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> + <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> + <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> + <?= Icon::create("star", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INACTIVE)->asImg(50) ?> + <? if ($allowed_to_review) : ?> + </a> + <? endif ?> + <? else : ?> + <? if ($allowed_to_review) : ?> + <a href="<?= $controller->link_for('oer/market/review/' . $material->getId()) ?>" data-dialog title="<?= sprintf(_('%s von 5 Sternen'), round($material['rating'] / 2, 1)) ?>"> + <? endif ?> + <? $material['rating'] = round($material['rating'], 1) / 2 ?> + <? $v = $material['rating'] >= 0.75 ? "" : ($material['rating'] >= 0.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> + <? $v = $material['rating'] >= 1.75 ? "" : ($material['rating'] >= 1.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> + <? $v = $material['rating'] >= 2.75 ? "" : ($material['rating'] >= 2.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> + <? $v = $material['rating'] >= 3.75 ? "" : ($material['rating'] >= 3.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> + <? $v = $material['rating'] >= 4.75 ? "" : ($material['rating'] >= 4.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", $allowed_to_review ? Icon::ROLE_CLICKABLE : Icon::ROLE_INFO)->asImg(50) ?> + <? if ($allowed_to_review) : ?> + </a> + <? endif ?> <? endif ?> - <? endif ?> - </div> + </div> - <ul class="reviews"> - <? foreach ($material->reviews as $review) : ?> - <li id="review_<?= $review->getId() ?>" class="review"> - <div class="avatar"> - <img width="50px" height="50px" src="<?= htmlReady($review['metadata']['host_id'] - ? ExternalUser::find($review['user_id'])->avatar_url - : Avatar::getAvatar($review['user_id'])->getURL(Avatar::MEDIUM)) ?>"> - </div> - <div class="content"> - <div class="timestamp"> - <?= date("j.n.Y G:i", $review['chdate']) ?> - </div> - <strong> - <? if ($review['metadata']['host_id']) : ?> - <? $user = ExternalUser::find($review['user_id']) ?> - <a href="<?= $controller->link_for("oer/market/profile/".$user->getId()) ?>"> - <?= htmlReady($user->name) ?> - </a> - <? else : ?> - <? $user = new User($review['user_id']) ?> - <a href="<?= URLHelper::getLink("dispatch.php/profile", ['username' => $user['username']]) ?>"> - <?= htmlReady($user->getFullName()) ?> - </a> - <? endif ?> - </strong> - <span class="origin">(<?= htmlReady($review['metadata']['host_id'] ? $review->host['name'] : Config::get()->UNI_NAME_CLEAN) ?>)</span> - <div class="review_text"> - <?= formatReady($review['content']) ?> + <ul class="reviews"> + <? foreach ($material->reviews as $review) : ?> + <li id="review_<?= $review->getId() ?>" class="review"> + <div class="avatar"> + <img width="50px" height="50px" src="<?= htmlReady($review['metadata']['host_id'] + ? ExternalUser::find($review['user_id'])->avatar_url + : Avatar::getAvatar($review['user_id'])->getURL(Avatar::MEDIUM)) ?>"> </div> - <div class="stars"> - <? $rating = round($review['metadata']['rating'], 1) ?> - <? $v = $rating >= 0.75 ? "" : ($rating >= 0.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> - <? $v = $rating >= 1.75 ? "" : ($rating >= 1.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> - <? $v = $rating >= 2.75 ? "" : ($rating >= 2.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> - <? $v = $rating >= 3.75 ? "" : ($rating >= 3.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> - <? $v = $rating >= 4.75 ? "" : ($rating >= 4.25 ? "-halffull" : "-empty") ?> - <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> + <div class="content"> + <div class="timestamp"> + <?= date("j.n.Y G:i", $review['chdate']) ?> + </div> + <strong> + <? if ($review['metadata']['host_id']) : ?> + <? $user = ExternalUser::find($review['user_id']) ?> + <a href="<?= $controller->link_for("oer/market/profile/".$user->getId()) ?>"> + <?= htmlReady($user->name) ?> + </a> + <? else : ?> + <? $user = new User($review['user_id']) ?> + <a href="<?= URLHelper::getLink("dispatch.php/profile", ['username' => $user['username']]) ?>"> + <?= htmlReady($user->getFullName()) ?> + </a> + <? endif ?> + </strong> + <span class="origin">(<?= htmlReady($review['metadata']['host_id'] ? $review->host['name'] : Config::get()->UNI_NAME_CLEAN) ?>)</span> + <div class="review_text"> + <?= formatReady($review['content']) ?> + </div> + <div class="stars"> + <? $rating = round($review['metadata']['rating'], 1) ?> + <? $v = $rating >= 0.75 ? "" : ($rating >= 0.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> + <? $v = $rating >= 1.75 ? "" : ($rating >= 1.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> + <? $v = $rating >= 2.75 ? "" : ($rating >= 2.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> + <? $v = $rating >= 3.75 ? "" : ($rating >= 3.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> + <? $v = $rating >= 4.75 ? "" : ($rating >= 4.25 ? "-halffull" : "-empty") ?> + <?= Icon::create("star$v", Icon::ROLE_INFO)->asImg(16) ?> - <? if ($GLOBALS['perm']->have_perm("autor") && !count($review->comments)) : ?> - <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>" style="font-size: 0.8em;"> - <?= _('Darauf antworten') ?> - </a> - <? endif ?> - </div> - <div class="comments center"> - <? if (count($review->comments)) : ?> - <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>"> - <?= Icon::create("comment", Icon::ROLE_CLICKABLE)->asImg(16, ['class' => "text-bottom"]) ?> - <?= sprintf(_('%s Kommentare dazu'), count($review->comments)) ?> - </a> - <? elseif ($material->isMine()) : ?> - <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>"> - <?= Icon::create("comment", Icon::ROLE_CLICKABLE)->asImg(16, ['class' => "text-bottom"]) ?> - <?= _('Dazu einen Kommentar schreiben') ?> - </a> - <? endif ?> + <? if ($GLOBALS['perm']->have_perm("autor") && !count($review->comments)) : ?> + <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>" style="font-size: 0.8em;"> + <?= _('Darauf antworten') ?> + </a> + <? endif ?> + </div> + <div class="comments center"> + <? if (count($review->comments)) : ?> + <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>"> + <?= Icon::create("comment", Icon::ROLE_CLICKABLE)->asImg(16, ['class' => "text-bottom"]) ?> + <?= sprintf(_('%s Kommentare dazu'), count($review->comments)) ?> + </a> + <? elseif ($material->isMine()) : ?> + <a href="<?= $controller->link_for("oer/market/discussion/".$review->getId()) ?>"> + <?= Icon::create("comment", Icon::ROLE_CLICKABLE)->asImg(16, ['class' => "text-bottom"]) ?> + <?= _('Dazu einen Kommentar schreiben') ?> + </a> + <? endif ?> + </div> </div> - </div> - </li> - <? endforeach ?> - </ul> + </li> + <? endforeach ?> + </ul> - <div class="center"> - <? if (!$material->isMine() && $GLOBALS['perm']->have_perm("autor")) : ?> - <?= \Studip\LinkButton::create(_('Review schreiben'), $controller->url_for('oer/market/review/' . $material->getId()), ['data-dialog' => 1]) ?> - <? endif ?> - </div> - </article> + <div class="center"> + <? if (!$material->isMine() && $GLOBALS['perm']->have_perm("autor")) : ?> + <?= \Studip\LinkButton::create(_('Review schreiben'), $controller->url_for('oer/market/review/' . $material->getId()), ['data-dialog' => 1]) ?> + <? endif ?> + </div> + </article> + <? endif ?> <? endif ?> - <? $actions = new ActionsWidget(); $GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS); diff --git a/db/migrations/5.2.9_add_oersi.php b/db/migrations/5.2.9_add_oersi.php new file mode 100644 index 0000000000000000000000000000000000000000..1b15d9ab2d2a6a969b3f5065ba3569a696014997 --- /dev/null +++ b/db/migrations/5.2.9_add_oersi.php @@ -0,0 +1,73 @@ +<?php + +class AddOersi extends Migration +{ + public function description () + { + return 'Adds the OER-search-index OERSI to OER Campus.'; + } + + public function up() + { + DBManager::get()->exec(" + ALTER TABLE `oer_hosts` + ADD COLUMN `sorm_class` varchar(50) DEFAULT 'OERHost' NOT NULL AFTER `host_id` + "); + DBManager::get()->exec(" + INSERT IGNORE INTO `oer_hosts` + SET `host_id` = MD5('oersi'), + `name` = 'OERSI', + `sorm_class` = 'OERHostOERSI', + `url` = 'https://oersi.de', + `public_key` = '', + `private_key` = '', + `active` = '1', + `index_server` = '1', + `allowed_as_index_server` = '1', + `last_updated` = UNIX_TIMESTAMP(), + `chdate` = UNIX_TIMESTAMP(), + `mkdate` = UNIX_TIMESTAMP() + "); + DBManager::get()->exec(" + ALTER TABLE `oer_material` + ADD COLUMN `source_url` varchar(256) DEFAULT NULL AFTER `published_id_on_twillo`, + ADD COLUMN `data` TEXT DEFAULT NULL AFTER `source_url` + "); + DBManager::get()->exec( + "INSERT IGNORE INTO `config` + (`field`, `value`, `type`, `range`, + `section`, + `mkdate`, `chdate`, + `description`) + VALUES + ('OER_OERSI_ONLY_DOWNLOADABLE', '1', 'boolean', 'global', + 'OERCampus', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), + 'Should the search in OERSI only find downloadable OERs?')" + ); + } + + public function down() + { + DBManager::get()->exec(" + ALTER TABLE `oer_hosts` + DROP COLUMN `sorm_class` + "); + DBManager::get()->exec(" + DELETE FROM `oer_hosts` + WHERE `host_id` = MD5('oersi') + "); + DBManager::get()->exec(" + ALTER TABLE `oer_material` + DROP COLUMN `source_url`, + DROP COLUMN `data` + "); + DBManager::get()->exec( + "DELETE FROM `config` + WHERE `field` = 'OER_OERSI_ONLY_DOWNLOADABLE'" + ); + DBManager::get()->exec( + "DELETE FROM `config_values` + WHERE `field` = 'OER_OERSI_ONLY_DOWNLOADABLE'" + ); + } +} diff --git a/lib/models/OERHost.php b/lib/models/OERHost.php index 187e49f6f6c6fc3d4e7312cb1567191e3697004d..bba555d1e76e494747a1df86188930e8e847fca7 100755 --- a/lib/models/OERHost.php +++ b/lib/models/OERHost.php @@ -13,7 +13,7 @@ class OERHost extends OERIdentity */ public static function thisOne() { - $host = self::findOneBySQL("private_key IS NOT NULL LIMIT 1"); + $host = self::findOneBySQL("`private_key` IS NOT NULL AND `sorm_class` = 'OERHost' LIMIT 1"); if ($host) { $host['url'] = $GLOBALS['oer_PREFERRED_URI'] ?: $GLOBALS['ABSOLUTE_URI_STUDIP']."dispatch.php/oer/endpoints/"; if ($host->isFieldDirty("url")) { @@ -39,6 +39,38 @@ class OERHost extends OERIdentity return self::findBySQL("1=1 ORDER BY name ASC"); } + public static function URIExists($uri) + { + return (bool) OERMaterial::findOneBySQL("LEFT JOIN `oer_hosts` USING (`host_id`) WHERE (`oer_hosts`.`sorm_class` = 'OERHost' OR `oer_hosts`.`sorm_class` IS NULL) AND `uri_hash` = ?", [ + md5($uri) + ]); + } + + public static function findBySQL($sql, $params = []) + { + $hosts = parent::findBySQL($sql, $params); + foreach ($hosts as $key => $host) { + $class = $host['sorm_class']; + if ($class && ($class !== 'OERHost') && is_subclass_of($class, 'OERHost')) { + $data = $host->toRawArray(); + $host = $class::buildExisting($data); + $hosts[$key] = $host; + } + } + return $hosts; + } + + public static function find($id) + { + $host = parent::find($id); + $class = $host['sorm_class']; + if ($class && ($class !== 'OERHost') && is_subclass_of($class, 'OERHost')) { + $data = $host->toRawArray(); + $host = $class::buildExisting($data); + } + return $host; + } + /** * configures this class * @param array $config @@ -111,7 +143,7 @@ class OERHost extends OERIdentity /** * Executes a search request on the host. - * @param string|null $text : the serach string + * @param string|null $text : the search string * @param string|null $tag : a tag to search for */ public function fetchRemoteSearch($text = null, $tag = null) @@ -203,9 +235,9 @@ class OERHost extends OERIdentity * @param string $foreign_material_id : foreign id of that oer-material * @return array|null : data of that material or null on error. */ - public function fetchItemData($foreign_material_id) + public function fetchItemData(OERMaterial $material) { - $endpoint_url = $this['url']."get_item_data/".urlencode($foreign_material_id); + $endpoint_url = $this['url']."get_item_data/".urlencode($material['foreign_material_id']); $output = @file_get_contents($endpoint_url); if ($output) { $output = json_decode($output, true); @@ -214,4 +246,55 @@ class OERHost extends OERIdentity } } } + + public function getFrontImageURL(OERMaterial $material) + { + return $this['url']."download_front_image/".$material['foreign_material_id']; + } + + public function isReviewable() + { + return true; + } + + public function getAuthorsForMaterial(OERMaterial $material) + { + $users = []; + foreach ($material->users as $materialdata) { + if ($materialdata['external_contact']) { + $user = $materialdata['oeruser']; + $users[] = [ + 'user_id' => $user['foreign_user_id'], + 'name' => $user['name'], + 'avatar' => $user['avatar'], + 'description' => $user['description'], + 'host_url' => $user->host['url'], + 'link' => URLHelper::getURL('dispatch.php/oer/market/profile/' . $user->getId()), + 'hostname' => $this['name'] + ]; + } else { + $user = User::find($materialdata['user_id']); + $users[] = [ + 'user_id' => $user['user_id'], + 'name' => $user ? $user->getFullName() : _('unbekannt'), + 'avatar' => Avatar::getAvatar($user['user_id'])->getURL(Avatar::NORMAL), + 'description' => $user ? $user['oercampus_description'] : '', + 'host_url' => OERHost::thisOne()->url, + 'link' => URLHelper::getURL('dispatch.php/profile', ['username' => $user['username']]), + 'hostname' => $this['name'] + ]; + } + } + return $users; + } + + public function getDownloadURLForMaterial(OERMaterial $material) + { + $base = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']); + $url = $material['host_id'] + ? $this->url . 'download/' . $material['foreign_material_id'] + : URLHelper::getURL('dispatch.php/oer/endpoints/download/' . $material->getId()); + URLHelper::setBaseURL($base); + return $url; + } } diff --git a/lib/models/OERHostOERSI.php b/lib/models/OERHostOERSI.php new file mode 100755 index 0000000000000000000000000000000000000000..3ceb23c4eeaf9a2e57afb70f0c9e21448588d3ad --- /dev/null +++ b/lib/models/OERHostOERSI.php @@ -0,0 +1,173 @@ +<?php + +class OERHostOERSI extends OERHost +{ + + /** + * Executes a search request on the host. + * @param string|null $text : the search string + * @param string|null $tag : a tag to search for + */ + public function fetchRemoteSearch($text = null, $tag = null) + { + $endpoint_url = 'https://oersi.de/resources/api-internal/search/oer_data/_search'; + $appendix = Config::get()->OER_OERSI_ONLY_DOWNLOADABLE ? ' AND _exists_:encoding.contentUrl' : ''; + if ($tag) { + $endpoint_url .= '?q=' . urlencode("keywords:" . $tag . $appendix); + } else { + $endpoint_url .= '?q=' . urlencode($text . $appendix); + } + $output = @file_get_contents($endpoint_url); + if ($output) { + $output = json_decode($output, true); + foreach ((array) $output['hits']['hits'] as $material_data) { + //check if material already in database from this or another OER Campus host + if (OERHost::URIExists($material_data['_source']['id'])) { + continue; + } + + $material = OERMaterial::findOneBySQL('foreign_material_id = ? AND host_id = ?', [ + md5($material_data['_source']['id']), + $this->getId() + ]); + if (!$material) { + $material = new OERMaterial(); + $material['foreign_material_id'] = md5($material_data['_source']['id']); + $material['host_id'] = $this->getId(); + } + $material['name'] = mb_substr($material_data['_source']['name'], 0, 64); + $material['draft'] = '0'; + $material['filename'] = ''; + $material['short_description'] = ''; + $material['description'] = $material_data['_source']['description'] ?: ''; + $material['difficulty_start'] = 0; + $material['difficulty_start'] = 12; + $material['uri'] = $material_data['_source']['id']; + $material['source_url'] = $material_data['_source']['id']; + $material['content_type'] = $material_data['_source']['encoding'][0]['encodingFormat'] ?: ''; + $material['license_identifier'] = $this->getLicenseID($material_data['_source']['license']['id']) ?: ''; + if (!$material['category']) { + $material['category'] = $material->autoDetectCategory(); + } + $material['front_image_content_type'] = $material_data['_source']['image'] ? 'image/jpg' : null; + $material['data'] = [ + 'front_image_url' => $material_data['_source']['image'], + 'download' => $material_data['_source']['encoding'][0]['contentUrl'] ?: '', + 'id' => $material_data['_id'], + 'authors' => $material_data['_source']['creator'], + 'organization' => $material_data['_source']['sourceOrganization'][0]['name'] ?: $material_data['_source']['publisher'][0]['name'] + ]; + $material->store(); + + //set topics: + //$material->setUsers([]); + + //set topics: + $material->setTopics($material_data['_source']['keywords']); + + } + } + } + + /** + * Pushes some data to the foreign OERHost. + * @param string $endpoint : part behind the host-url like "push_data" + * @param array $data : the data to be pushed as an associative array + * @return bool|CurlHandle|resource + */ + public function pushDataToEndpoint($endpoint, $data) + { + //nothing to do + } + + /** + * Fetches all information of an item from that host. This is a request. + * @param string $foreign_material_id : foreign id of that oer-material + * @return array|null : data of that material or null on error. + */ + public function fetchItemData(OERMaterial $material) + { + $endpoint_url = 'https://oersi.de/resources/' . urlencode($material['data']['id']) . '?format=json'; + $output = @file_get_contents($endpoint_url); + if ($output) { + $output = json_decode($output, true); + if ($output) { + $data = []; + $data['name'] = mb_substr($output['name'], 0, 64); + $data['draft'] = '0'; + $data['filename'] = ''; + $data['short_description'] = ''; + $data['description'] = $output['description'] ?: ''; + $data['difficulty_start'] = 0; + $data['difficulty_start'] = 12; + $data['uri'] = $output['encoding'][0]['contentUrl'] ?: ''; + $data['source_url'] = $output['id']; + $data['content_type'] = $output['encoding'][0]['encodingFormat'] ?: ''; + $data['license_identifier'] = $this->getLicenseID($output['license']['id']) ?: ''; + if (!$data['category']) { + $data['category'] = $material->autoDetectCategory(); + } + $data['front_image_content_type'] = $output['image'] ? 'image/jpg' : null; + $data['data'] = $material['data']->getArrayCopy(); + $data['data']['download'] = $output['encoding'][0]['contentUrl'] ?: ''; + $data['data']['front_image_url'] = $output['image']; + $data['data']['authors'] = $output['creator']; + $data['data']['organization'] = $output['sourceOrganization'][0]['name'] ?: $output['publisher'][0]['name']; + $data = [ + 'data' => $data, + 'topics' => $output['keywords'] + ]; + return $data; + } + } else { + return ['deleted' => 1]; + } + } + + public function getFrontImageURL(OERMaterial $material) + { + return $material['data'] ? $material['data']['front_image_url'] : null; + } + + /** + * Tries to match the CC-license URL from OERSI to an spdx-identifier, which is used in Stud.IP + * @param $license : an URL + * @return string|null + */ + protected function getLicenseID($license) + { + preg_match("^https:\/\/creativecommons.org\/licenses\/([\w\d\-\.]+)\/([\w\d\-\.]+)^", $license, $matches); + if ($matches[0]) { + $spdx_id = 'CC-' . strtoupper($matches[1]) . '-' . strtoupper($matches[2]); + if (License::find($spdx_id)) { + return $spdx_id; + } + } + return null; + } + + public function isReviewable() + { + return false; + } + + public function getAuthorsForMaterial(OERMaterial $material) + { + $users = []; + $data = $material->data->getArrayCopy(); + foreach ((array) $data['authors'] as $author) { + $users[] = [ + 'name' => $author['name'], + 'hostname' => $data['organization'] ?: $this['name'] + ]; + } + return $users; + } + + public function getDownloadURLForMaterial(OERMaterial $material) + { + return $material['uri']; + } + + +} diff --git a/lib/models/OERMaterial.php b/lib/models/OERMaterial.php index d718d60ebbb070b43eca5631882de7d421fd9f2f..7612f70f9853ad411cc6cb40333ea58016f8865e 100755 --- a/lib/models/OERMaterial.php +++ b/lib/models/OERMaterial.php @@ -23,6 +23,7 @@ class OERMaterial extends SimpleORMap 'foreign_key' => 'license_identifier' ]; $config['serialized_fields']['structure'] = 'JSONArrayObject'; + $config['serialized_fields']['data'] = 'JSONArrayObject'; $config['registered_callbacks']['before_store'][] = "cbHashURI"; $config['registered_callbacks']['before_delete'][] = "cbDeleteFile"; parent::configure($config); @@ -145,9 +146,11 @@ class OERMaterial extends SimpleORMap $id = $matches[1]; $material = OERMaterial::find($id); - $url = $material['host_id'] - ? $material->host->url."download/".$material['foreign_material_id'] - : URLHelper::getURL("dispatch.php/oer/endpoints/download/".$material->getId()); + if (!$material) { + return _('OER Material nicht vorhanden.'); + } + + $url = $material->getDownloadUrl(); if ($material['player_url'] || $material->isPDF()) { if ($material['player_url']) { @@ -171,6 +174,7 @@ class OERMaterial extends SimpleORMap $template = $tf->open("oer/embed/standard"); } $template->url = $url; + $template->source_url = $material['source_url']; $template->material = $material; $template->id = $id; return $template->render(); @@ -184,7 +188,16 @@ class OERMaterial extends SimpleORMap public function cbHashURI() { - $this['uri_hash'] = md5($this['uri']); + $this['uri_hash'] = $this['uri'] ? md5($this['uri']) : ''; + } + + public function getAuthors() + { + if ($this->host) { + return $this->host->getAuthorsForMaterial($this); + } else { + return OERHost::thisOne()->getAuthorsForMaterial($this); + } } public function getTopics() @@ -217,7 +230,7 @@ class OERMaterial extends SimpleORMap SET tag_hash = MD5(:tag), material_id = :material_id "); - foreach ($tags as $tag) { + foreach ((array) $tags as $tag) { $insert_tag->execute([ 'tag' => $tag ]); @@ -238,11 +251,15 @@ class OERMaterial extends SimpleORMap public function getDownloadUrl() { - $base = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']); - $url = $this['host_id'] - ? $this->host->url."download/".$this['foreign_material_id'] - : URLHelper::getURL("dispatch.php/oer/endpoints/download/".$this->getId()); - URLHelper::setBaseURL($base); + if (!$this['host_id']) { + $base = URLHelper::setBaseURL($GLOBALS['ABSOLUTE_URI_STUDIP']); + $url = $this['host_id'] + ? $this->host->url . 'download/' . $this['foreign_material_id'] + : URLHelper::getURL('dispatch.php/oer/endpoints/download/' . $this->getId()); + URLHelper::setBaseURL($base); + } else { + $url = $this->host->getDownloadURLForMaterial($this); + } return $url; } @@ -261,7 +278,7 @@ class OERMaterial extends SimpleORMap { if ($this['front_image_content_type']) { if ($this['host_id']) { - return $this->host['url']."download_front_image/".$this['foreign_material_id']; + return $this->host->getFrontImageURL($this); } else { return URLHelper::getURL("dispatch.php/oer/endpoints/download_front_image/".$this->getId()); } @@ -357,10 +374,7 @@ class OERMaterial extends SimpleORMap unset($data['data']['id']); unset($data['data']['user_id']); unset($data['data']['host_id']); - $data['users'] = []; - foreach ($this->users as $materialuser) { - $data['users'][] = $materialuser->getJSON(); - } + $data['users'] = $myHost->getAuthorsForMaterial($this); $data['topics'] = []; foreach ($this->getTopics() as $tag) { if ($tag['name']) { @@ -400,93 +414,90 @@ class OERMaterial extends SimpleORMap public function fetchData() { - if ($this['host_id']) { - $host = new OERHost($this['host_id']); - if ($host) { - $data = $host->fetchItemData($this['foreign_material_id']); + if ($this['host_id'] && $this->host) { + $data = $this->host->fetchItemData($this); - if (!$data) { - return false; - } + if (!$data) { + return false; + } - if ($data['deleted']) { - return "deleted"; - } + if ($data['deleted']) { + return "deleted"; + } - //material: - $material_data = $data['data']; - unset($material_data['material_id']); - unset($material_data['mkdate']); - $this->setData($material_data); - $this->store(); - - //topics: - $this->setTopics($data['topics']); - - //user: - $this->setUsers($data['users']); - - foreach ((array) $data['reviews'] as $review_data) { - $currenthost = OERHost::findOneByUrl(trim($review_data['host']['url'])); - if (!$currenthost) { - $currenthost = new OERHost(); - $currenthost['url'] = trim($review_data['host']['url']); - $currenthost['last_updated'] = time(); - $currenthost->fetchPublicKey(); - if ($currenthost['public_key']) { - $currenthost->store(); - } + //material: + $material_data = $data['data']; + unset($material_data['material_id']); + unset($material_data['mkdate']); + $this->setData($material_data); + $this->store(); + + //topics: + $this->setTopics($data['topics']); + + //user: + $this->setUsers($data['users']); + + foreach ((array) $data['reviews'] as $review_data) { + $currenthost = OERHost::findOneByUrl(trim($review_data['host']['url'])); + if (!$currenthost) { + $currenthost = new OERHost(); + $currenthost['url'] = trim($review_data['host']['url']); + $currenthost['last_updated'] = time(); + $currenthost->fetchPublicKey(); + if ($currenthost['public_key']) { + $currenthost->store(); + } + } + if ($currenthost && $currenthost['public_key'] && !$currenthost->isMe()) { + $review = OERReview::findOneBySQL(" + context_id = :context_id + AND `metadata` LIKE :foreign_review_id + AND `metadata` LIKE :host_id", [ + 'context_id' => $this->getId(), + 'foreign_review_id' => "%".$review_data['foreign_review_id']."%", + 'host_id' => "%".$currenthost->getId()."%" + ]); + if (!$review) { + $review = new OERReview(); + $review['context_id'] = $this->getId(); + $review['context_type'] = "public"; + $review['display_class'] = "OERReview"; + $review['visible_in_stream'] = 0; + $review['commentable'] = 1; + } + $review['content'] = $review_data['review']; + $review['metadata'] = [ + 'host_id' => $currenthost->getId(), + 'foreign_review_id' => $review_data['foreign_review_id'], + 'rating' => $review_data['rating'] + ]; + if ($review_data['chdate']) { + $review['chdate'] = $review_data['chdate']; } - if ($currenthost && $currenthost['public_key'] && !$currenthost->isMe()) { - $review = OERReview::findOneBySQL(" - context_id = :context_id - AND `metadata` LIKE :foreign_review_id - AND `metadata` LIKE :host_id", [ - 'context_id' => $this->getId(), - 'foreign_review_id' => "%".$review_data['foreign_review_id']."%", - 'host_id' => "%".$currenthost->getId()."%" - ]); - if (!$review) { - $review = new OERReview(); - $review['context_id'] = $this->getId(); - $review['context_type'] = "public"; - $review['display_class'] = "OERReview"; - $review['visible_in_stream'] = 0; - $review['commentable'] = 1; - } - $review['content'] = $review_data['review']; - $review['metadata'] = [ - 'host_id' => $currenthost->getId(), - 'foreign_review_id' => $review_data['foreign_review_id'], - 'rating' => $review_data['rating'] - ]; - if ($review_data['chdate']) { - $review['chdate'] = $review_data['chdate']; - } - if ($review_data['mkdate']) { - $review['mkdate'] = $review_data['mkdate']; - } - - $user = ExternalUser::findOneBySQL("foreign_id = :foreign_id AND host_id = :host_id", [ - 'foreign_id' => $review_data['user']['user_id'], - 'host_id' => $currenthost->getId() - ]); - - if (!$user) { - $user = new ExternalUser(); - $user['foreign_id'] = $review_data['user']['user_id']; - $user['host_id'] = $currenthost->getId(); - } - $user['contact_type'] = "oercampus"; - $user['name'] = $review_data['user']['name']; - $user['avatar_url'] = $review_data['user']['avatar'] ?: null; - $user['data']['description'] = $review_data['user']['description'] ?: null; - $user->store(); - - $review['user_id'] = $user->getId(); - - $review->store(); + if ($review_data['mkdate']) { + $review['mkdate'] = $review_data['mkdate']; } + + $user = ExternalUser::findOneBySQL("foreign_id = :foreign_id AND host_id = :host_id", [ + 'foreign_id' => $review_data['user']['user_id'], + 'host_id' => $currenthost->getId() + ]); + + if (!$user) { + $user = new ExternalUser(); + $user['foreign_id'] = $review_data['user']['user_id']; + $user['host_id'] = $currenthost->getId(); + } + $user['contact_type'] = "oercampus"; + $user['name'] = $review_data['user']['name']; + $user['avatar_url'] = $review_data['user']['avatar'] ?: null; + $user['data']['description'] = $review_data['user']['description'] ?: null; + $user->store(); + + $review['user_id'] = $user->getId(); + + $review->store(); } } } diff --git a/lib/models/OERMaterialUser.php b/lib/models/OERMaterialUser.php index 1e6babe4e2f2f1406ba1ea80b04608e2818e3b0e..9b4e1aa2829e11b58dd7d1952e7e92d46f0e3198 100755 --- a/lib/models/OERMaterialUser.php +++ b/lib/models/OERMaterialUser.php @@ -11,29 +11,10 @@ class OERMaterialUser extends SimpleORMap 'foreign_key' => 'user_id' ]; + $config['belongs_to']['material'] = [ + 'class_name' => OERMaterial::class, + 'foreign_key' => 'material_id' + ]; parent::configure($config); } - - public function getJSON() - { - if ($this['external_contact']) { - $user = $this['oeruser']; - return [ - 'user_id' => $user['foreign_user_id'], - 'name' => $user['name'], - 'avatar' => $user['avatar'], - 'description' => $user['description'], - 'host_url' => $user->host['url'] - ]; - } else { - $user = User::find($this['user_id']); - return [ - 'user_id' => $user['user_id'], - 'name' => $user ? $user->getFullName() : _("unbekannt"), - 'avatar' => Avatar::getAvatar($user['user_id'])->getURL(Avatar::NORMAL), - 'description' => $user ? $user['oercampus_description'] : "", - 'host_url' => OERHost::thisOne()->url - ]; - } - } } diff --git a/lib/modules/CoreDocuments.class.php b/lib/modules/CoreDocuments.class.php index 28fcae5cce7322d58d039227133e8bc008ccfb71..c7aff2dc05e855800a82649c527c147933154573 100644 --- a/lib/modules/CoreDocuments.class.php +++ b/lib/modules/CoreDocuments.class.php @@ -50,19 +50,30 @@ class CoreDocuments extends CorePlugin implements StudipModule, OERModule 'content_terms_of_use_id' => "FREE_LICENSE", 'description' => $material['description'] ]; - if ($material['host_id']) { - $tmp_name = $GLOBALS['TMP_PATH']."/oer_".$material->getId(); - file_put_contents($tmp_name, file_get_contents($material->getDownloadUrl())); - $uploaded_file['tmp_name'] = $tmp_name; - $uploaded_file['type'] = filesize($tmp_name); - } else { - $uploaded_file['tmp_name'] = $material->getFilePath(); - $uploaded_file['size'] = filesize($material->getFilePath()); - } + $url = $material->getDownloadUrl(); + if ($url) { + if ($material['host_id']) { + $tmp_name = $GLOBALS['TMP_PATH'] . '/oer_' . $material->getId(); + file_put_contents($tmp_name, file_get_contents($url)); + $uploaded_file['tmp_name'] = $tmp_name; + $uploaded_file['type'] = filesize($tmp_name); + } else { + $uploaded_file['tmp_name'] = $material->getFilePath(); + $uploaded_file['size'] = filesize($material->getFilePath()); + } - $standardfile = StandardFile::create($uploaded_file); + $standardfile = StandardFile::create($uploaded_file); + } elseif($material['source_url']) { + $standardfile = URLFile::create([ + 'url' => $material['source_url'], + 'name' => $material['name'], + 'author_name' => implode(', ', array_map(function ($a) { return $a['name']; }, $material)), + 'description' => $material['description'], + 'content_terms_of_use_id' => "FREE_LICENSE" + ]); + } - if ($standardfile->getSize()) { + if ($standardfile->getSize() || is_a($standardfile, 'URLFile')) { $error = $folder->validateUpload($standardfile, User::findCurrent()->id); if ($error && is_string($error)) { if ($tmp_name) { diff --git a/resources/assets/javascripts/lib/oer.js b/resources/assets/javascripts/lib/oer.js index 701a37b90a2af89f1108e71723fa70ae795e95da..dac19ec56ad17cf652330bb68851b8ed7d753741 100755 --- a/resources/assets/javascripts/lib/oer.js +++ b/resources/assets/javascripts/lib/oer.js @@ -186,6 +186,13 @@ const OER = { }, getMaterialURL: function (material_id) { return this.material_select_url_template.replace("__material_id__", material_id); + }, + shortenName: function (name) { + if (name.length > 55) { + return name.substring(0, 50) + ' ...'; + } else { + return name; + } } }, mounted: function () { diff --git a/resources/assets/stylesheets/scss/oer.scss b/resources/assets/stylesheets/scss/oer.scss index 5e2df197e0e843503906d9dac8f24e5f3780dddf..6af5b26b1b161b79246b3a6a62c892fdddb85e0f 100755 --- a/resources/assets/stylesheets/scss/oer.scss +++ b/resources/assets/stylesheets/scss/oer.scss @@ -373,10 +373,6 @@ ul.reviews, ol.reviews { display: none !important; } - article.contentbox { - animation: oer-material-appears 200ms ease-out; - } - .browser { margin-top: 15px; padding: 10px; @@ -462,18 +458,6 @@ ul.reviews, ol.reviews { } } -@keyframes oer-material-appears { - from { - opacity: 0; - max-width: 0px; - overflow: hidden; - } - to { - overflow: hidden; - max-width: 270px; - opacity: 1; - } -} @keyframes oer-tag-appears { from {