<?php
namespace AppBundle\Controller;
use AppBundle\Common\ArrayToolkit;
use AppBundle\Common\Paginator;
use AppBundle\Component\MediaParser\ParserProxy;
use Biz\Course\MaterialException;
use Biz\Course\Service\CourseService;
use Biz\Course\Service\CourseSetService;
use Biz\Course\Service\MaterialService;
use Biz\Favorite\Service\FavoriteService;
use Biz\File\Service\UploadFileService;
use Biz\Goods\Service\GoodsService;
use Biz\OpenCourse\OpenCourseException;
use Biz\OpenCourse\Service\OpenCourseRecommendedService;
use Biz\System\Service\SettingService;
use Biz\Taxonomy\Service\TagService;
use Biz\Thread\Service\ThreadService;
use Biz\User\Service\AuthService;
use Biz\User\Service\TokenService;
use Biz\User\Service\UserService;
use Biz\User\UserException;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class OpenCourseController extends BaseOpenCourseController
{
public function createAction(Request $request)
{
$course = $request->request->all();
unset($course['buyable']);
$course = $this->getOpenCourseService()->createCourse($course);
return $this->redirect($this->generateUrl('open_course_manage', ['id' => $course['id']]));
}
public function showAction(Request $request, $courseId, $lessonId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
$preview = $request->query->get('as');
$isWxPreview = 'preview' === $request->query->get('as') && 'wx' === $request->query->get('previewType');
$tags = $this->getTagService()->findTagsByOwner(['ownerType' => 'openCourse', 'ownerId' => $courseId]);
$tagIds = ArrayToolkit::column($tags, 'id');
if ($isWxPreview || $this->isWxClient()) {
$template = 'open-course/mobile/open-course-show.html.twig';
} else {
$template = 'open-course/open-course-show.html.twig';
}
if ('preview' === $preview) {
$this->getOpenCourseService()->tryManageOpenCourse($courseId);
return $this->render($template, [
'tagIds' => $tagIds,
'course' => $course,
'lessonId' => $lessonId,
'wxPreviewUrl' => $this->getWxPreviewQrCodeUrl($course['id']),
]);
}
$result = $this->_checkCourseStatus($courseId);
if (!$result['status']) {
return $this->createMessageResponse('error', $result['message']);
}
$this->_memberOperate($request, $courseId);
$course = $this->getOpenCourseService()->waveCourse($courseId, 'hitNum', +1);
$response = $this->renderView($template, [
'tagIds' => $tagIds,
'course' => $course,
'lessonId' => $lessonId,
]);
$response = new Response($response);
if (!$request->cookies->get('uv')) {
$expire = strtotime(date('Y-m-d').' 23:59:59');
$response->headers->setCookie(new Cookie('uv', uniqid($prefix = 'refererToken'), $expire));
//$response->send();
}
if ('liveOpen' != $course['type']) {
$this->createRefererLog($request, $course);
}
return $response;
}
public function lessonShowAction(Request $request, $courseId, $lessonId)
{
$lesson = $this->getOpenCourseService()->getLesson($lessonId);
if (!$lesson) {
return $this->createMessageResponse('error', '该课时不存在!');
}
if ($lesson['mediaId'] && 'self' == $lesson['mediaSource']) {
$file = $this->getUploadFileService()->getFile($lesson['mediaId']);
if (!$file) {
return $this->createJsonResponse(['mediaError' => '该课时为无效课时,不能播放']);
}
} elseif (0 == $lesson['mediaId'] && 'self' == $lesson['mediaSource']) {
return $this->createJsonResponse(['mediaError' => '该课时为无效课时,不能播放']);
}
$lesson = $this->_getLessonVedioInfo($request, $lesson);
return $this->createJsonResponse($lesson);
}
/**
* Block Actions.
*/
public function headerAction(Request $request, $course, $lessonId)
{
$isWxPreview = 'preview' === $request->query->get('as') && 'wx' === $request->query->get('previewType');
if ($isWxPreview || $this->isWxClient()) {
$template = 'open-course/mobile/open-course-header.html.twig';
} else {
$template = 'open-course/open-course-header.html.twig';
}
if ($lessonId) {
$lesson = $this->getOpenCourseService()->getCourseLesson($course['id'], $lessonId);
if (!$lesson || ('preview' !== $request->query->get('as') && 'published' != $lesson['status'])) {
$lesson = [];
}
} else {
$lesson = $this->_checkPublishedLessonExists($course['id']);
}
$lesson = $lesson ? $this->_getLessonVedioInfo($request, $lesson) : [];
//$nextLesson = $this->getOpenCourseService()->getNextLesson($course['id'], $lesson['id']);
$member = $this->_getMember($course['id']);
if ($lesson) {
$lesson['replays'] = $this->_getLiveReplay($lesson);
}
$notifyNum = $this->getOpenCourseService()->countMembers(['courseId' => $course['id'], 'isNotified' => 1]);
return $this->render($template, [
'course' => $course,
'lesson' => $lesson,
'member' => $member,
'notifyNum' => $notifyNum,
// 'nextLesson' => $nextLesson
]);
}
public function teachersAction($courseId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
$teachersNoSort = $this->getUserService()->findUsersByIds($course['teacherIds']);
$teachers = [];
if (!empty($course['teacherIds'][0])) {
foreach ($course['teacherIds'] as $key => $teacherId) {
$teachers[$teacherId] = $teachersNoSort[$teacherId];
}
}
$profiles = $this->getUserService()->findUserProfilesByIds($course['teacherIds']);
return $this->render('open-course/open-course-teacher-block.html.twig', [
'course' => $course,
'teachers' => $teachers,
'profiles' => $profiles,
]);
}
public function infoBarAction(Request $request, $courseId, $lessonId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
$lesson = empty($lessonId) ? [] : $this->getOpenCourseService()->getCourseLesson($courseId, $lessonId);
$member = $this->_getMember($course['id']);
$memberFavorite = $this->getFavoriteService()->getUserFavorite($this->getCurrentUser()->getId(), 'openCourse', $courseId);
return $this->render('open-course/info-bar-block.html.twig', [
'course' => $course,
'member' => $member,
'memberFavorite' => $memberFavorite,
'lesson' => $lesson,
]);
}
public function likeAction(Request $request, $id)
{
$result = $this->_checkCourseStatus($id);
if (!$result['status']) {
return $this->createJsonResponse(['result' => false]);
}
$course = $this->getOpenCourseService()->waveCourse($id, 'likeNum', +1);
return $this->createJsonResponse(['result' => true, 'number' => $course['likeNum']]);
}
public function unlikeAction(Request $request, $id)
{
$result = $this->_checkCourseStatus($id);
if (!$result['status']) {
return $this->createJsonResponse(['result' => false]);
}
$course = $this->getOpenCourseService()->waveCourse($id, 'likeNum', -1);
return $this->createJsonResponse(['result' => true, 'number' => $course['likeNum']]);
}
protected function getWxPreviewQrCodeUrl($id)
{
$user = $this->getUserService()->getCurrentUser();
$token = $this->getTokenService()->makeToken('qrcode', [
'userId' => $user['id'],
'data' => [
'url' => $this->generateUrl(
'open_course_show',
[
'courseId' => $id,
'as' => 'preview',
],
UrlGeneratorInterface::ABSOLUTE_URL),
'appUrl' => '',
],
'times' => 0,
'duration' => 3600,
]);
$url = $this->generateUrl('common_parse_qrcode', ['token' => $token['token']], UrlGeneratorInterface::ABSOLUTE_URL);
return $url;
}
public function commentAction(Request $request, $courseId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
if (!$course) {
return $this->createMessageResponse('error', '课程不存在,或未发布。');
}
$conditions = [
'targetId' => $course['id'],
'targetType' => 'openCourse',
'parentId' => 0,
'excludeAuditStatus' => 'illegal',
];
$paginator = new Paginator(
$request,
$this->getThreadService()->searchPostsCount($conditions),
10
);
$posts = $this->getThreadService()->searchPosts(
$conditions,
['createdTime' => 'ASC'],
$paginator->getOffsetCount(),
$paginator->getPerPageCount()
);
$users = $this->getUserService()->findUsersByIds(ArrayToolkit::column($posts, 'userId'));
$isWxPreview = 'preview' === $request->query->get('as') && 'wx' === $request->query->get('previewType');
if ($isWxPreview || $this->isWxClient()) {
$template = 'open-course/mobile/open-course-comment.html.twig';
} else {
$template = 'open-course/open-course-comment.html.twig';
}
return $this->render($template, [
'course' => $course,
'posts' => $posts,
'users' => $users,
'paginator' => $paginator,
'service' => $this->getThreadService(),
'goto' => $this->generateUrl('open_course_show', ['courseId' => $course['id']]),
]);
}
public function postAction(Request $request, $id)
{
$result = $this->_checkCourseStatus($id);
if (!$result['status']) {
return $this->createMessageResponse('error', $result['message']);
}
return $this->forward('AppBundle:Thread:postSave', [
'request' => $request,
'targetType' => 'openCourse',
'targetId' => $id,
]);
}
public function postReplyAction(Request $request, $id, $postId)
{
$result = $this->_checkCourseStatus($id);
if (!$result['status']) {
return $this->createMessageResponse('error', $result['message']);
}
$fields = $request->request->all();
if(!$this->checkDragCaptchaToken($request, $fields['_dragCaptchaToken'])){
return $this->createJsonResponse(['error' => ['code'=> 403, 'message' => $this->trans("exception.form..drag.expire")]], 403);
}
unset($fields['_dragCaptchaToken']);
$fields['content'] = $this->autoParagraph($fields['content']);
$fields['targetId'] = $id;
$fields['parentId'] = $postId;
$fields['targetType'] = 'openCourse';
$post = $this->getThreadService()->createPost($fields);
return $this->render('thread/subpost-item.html.twig', [
'post' => $post,
'author' => $this->getCurrentUser(),
'service' => $this->getThreadService(),
]);
}
public function memberSmsAction(Request $request, $id)
{
$course = $this->getOpenCourseService()->getCourse($id);
$user = $this->getCurrentUser();
if (!$course) {
return $this->createJsonResponse(['result' => false, 'message' => '该课程不存在或已删除!']);
}
$smsSetting = $this->setting('cloud_sms', []);
if (!$user->isLogin() && !$smsSetting['sms_enabled']) {
$this->createNewException(UserException::PERMISSION_DENIED());
}
if ('POST' == $request->getMethod()) {
$member = $this->_memberOperate($request, $id);
$fields = $request->request->all();
$fields['isNotified'] = 1;
$member = $this->getOpenCourseService()->updateMember($member['id'], $fields);
$this->_loginMemberMobileBind($fields['mobile']);
$memberNum = $this->getOpenCourseService()->countMembers(['courseId' => $id, 'isNotified' => 1]);
return $this->createJsonResponse(['result' => true, 'number' => $memberNum]);
}
return $this->render('open-course/member-sms-modal.html.twig', [
'course' => $course,
]);
}
public function createMemberAction(Request $request, $id)
{
$result = $this->_checkExistsMember($request, $id);
if (!$result['result']) {
return $this->createJsonResponse($result);
}
if ('POST' == $request->getMethod()) {
$fields = $request->request->all();
$fields['ip'] = $request->getClientIp();
$fields['courseId'] = $id;
$member = $this->getOpenCourseService()->createMember($fields);
$memberNum = $this->getOpenCourseService()->countMembers(['courseId' => $id]);
return $this->createJsonResponse(['result' => true, 'number' => $memberNum]);
}
}
public function playerAction(Request $request, $courseId, $lessonId)
{
$lesson = $this->getOpenCourseService()->getCourseLesson($courseId, $lessonId);
if (empty($lesson)) {
$this->createNewException(OpenCourseException::NOTFOUND_LESSON());
}
if ('liveOpen' == $lesson['type'] && 'videoGenerated' == $lesson['replayStatus']) {
if (empty($lesson['replayEnable'])) {
return $this->createMessageResponse('error', '直播回放被设置为不允许观看!');
}
$course = $this->getOpenCourseService()->getCourse($courseId);
$this->createRefererLog($request, $course);
}
return $this->forward('AppBundle:Player:show', [
'id' => $lesson['mediaId'],
'context' => ['hideBeginning' => 1, 'hideQuestion' => 1],
]);
}
public function materialListAction(Request $request, $id)
{
$course = $this->getOpenCourseService()->getCourse($id);
$conditions = [
'courseId' => $id,
'excludeLessonId' => 0,
'source' => 'opencoursematerial',
'type' => 'openCourse',
];
$materials = $this->getMaterialService()->searchMaterials(
$conditions,
['createdTime' => 'DESC'],
0,
PHP_INT_MAX
);
$lessons = $this->getOpenCourseService()->findLessonsByCourseId($course['id']);
$lessons = ArrayToolkit::index($lessons, 'id');
return $this->render('open-course/open-course-material-block.html.twig', [
'course' => $course,
'lessons' => $lessons,
'materials' => $materials,
]);
}
public function materialDownloadAction(Request $request, $courseId, $materialId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
$material = $this->getMaterialService()->getMaterial($courseId, $materialId);
if (empty($material)) {
$this->createNewException(MaterialException::NOTFOUND_MATERIAL());
}
if ('opencourselesson' == $material['source'] || !$material['lessonId']) {
return $this->createMessageResponse('error', '无权下载该资料');
}
return $this->forward('AppBundle:UploadFile:download', ['fileId' => $material['fileId']]);
}
public function mobileCheckAction(Request $request, $courseId)
{
$user = $this->getCurrentUser();
$response = ['success' => true, 'message' => ''];
$mobile = $request->query->get('value', '');
$member = $this->getOpenCourseService()->getCourseMemberByMobile($courseId, $mobile);
if ($member && $member['isNotified']) {
return $this->createJsonResponse(['success' => false, 'message' => '该手机号已报名']);
}
if ($user->isLogin()) {
list($result, $message) = $this->getAuthService()->checkMobile($mobile);
if ('success' != $result) {
return $this->createJsonResponse(['success' => false, 'message' => $message]);
}
}
return $this->createJsonResponse($response);
}
public function adModalRecommendCourseAction(Request $request, $id)
{
$num = $request->query->get('num', 3);
$goodses = $this->getOpenCourseRecommendedService()->findRandomRecommendGoods($id, $num);
$goodses = array_values($goodses);
$conditions = [
[
'status' => 'published',
'recommendedTime_GT' => 0,
],
[
'status' => 'published',
],
];
$orderBys = [
[
'recommendWeight' => 'ASC',
'recommendedTime' => 'DESC',
],
[],
];
//数量不够 凑数
foreach ($conditions as $key => $condition) {
if (count($goodses) < $num) {
$needNum = $num - count($goodses);
$condition['excludeIds'] = ArrayToolkit::column($goodses, 'id');
$newGoodses = $this->getGoodsService()->searchGoods($condition, $orderBys[$key], 0, $needNum);
$goodses = array_merge($goodses, $newGoodses);
}
}
$self = $this;
$goodses = array_map(function ($goods) use ($self) {
foreach (['small', 'middle', 'large'] as $coverType) {
$picturePath = empty($goods['images'][$coverType]) ? '' : $goods['images'][$coverType];
$goods['cover'][$coverType] = $self->get('web.twig.extension')->getFpath($picturePath, 'course.png');
}
return $goods;
}, $goodses);
return $this->createJsonResponse($goodses);
}
private function _getMember($courseId)
{
$user = $this->getCurrentUser();
$member = [];
if ($user->isLogin()) {
$member = $this->getOpenCourseService()->getCourseMember($courseId, $user['id']);
} /* else {
$member = $this->getOpenCourseService()->getCourseMemberByIp($courseId, $user['currentIp']);
}*/
return $member;
}
private function _getLessonVedioInfo(Request $request, $lesson)
{
$lesson['videoWatermarkEmbedded'] = 0;
if (('video' == $lesson['type'] || ('liveOpen' == $lesson['type'] && 'videoGenerated' == $lesson['replayStatus'])) && 'self' == $lesson['mediaSource']) {
$file = $this->getUploadFileService()->getFullFile($lesson['mediaId']);
if ($file) {
$lesson['convertStatus'] = empty($file['convertStatus']) ? 'none' : $file['convertStatus'];
$lesson['storage'] = $file['storage'];
}
} elseif (in_array($lesson['mediaSource'], ['youku', 'NeteaseOpenCourse', 'qqvideo'])) {
$proxy = new ParserProxy();
$lesson = $proxy->prepareMediaUri($lesson);
}
if ('liveOpen' == $lesson['type']) {
if ($lesson['startTime'] > time()) {
$lesson['startTimeLeft'] = $lesson['startTime'] - time();
}
}
return $lesson;
}
private function _checkCourseStatus($courseId)
{
$course = $this->getOpenCourseService()->getCourse($courseId);
if (empty($course)) {
return ['status' => false, 'message' => '课程已删除,请联系管理员再试。'];
}
if ('published' != $course['status']) {
return ['status' => false, 'message' => '课程已关闭,请联系管理员再试。'];
}
return ['status' => true, 'message' => ''];
}
private function _checkPublishedLessonExists($courseId)
{
$lessons = $this->getOpenCourseService()->searchLessons([
'courseId' => $courseId,
'status' => 'published',
],
['seq' => 'ASC'], 0, 1
);
if (!$lessons) {
return false;
}
return $lessons[0];
}
protected function autoParagraph($text)
{
if ('' !== trim($text)) {
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$text = preg_replace("/\n\n+/", "\n\n", str_replace(["\r\n", "\r"], "\n", $text));
$texts = preg_split('/\n\s*\n/', $text, -1, PREG_SPLIT_NO_EMPTY);
$text = '';
foreach ($texts as $txt) {
$text .= '<p>'.nl2br(trim($txt, "\n"))."</p>\n";
}
$text = preg_replace('|<p>\s*</p>|', '', $text);
}
return $text;
}
private function _getLiveReplay($lesson)
{
$replays = [];
if (in_array($lesson['type'], ['liveOpen', 'replay'])) {
$replays = $this->getLiveReplayService()->searchReplays([
'courseId' => $lesson['courseId'],
'lessonId' => $lesson['id'],
'hidden' => 0,
'type' => 'liveOpen',
], ['createdTime' => 'DESC'], 0, PHP_INT_MAX);
}
return $replays;
}
private function _loginMemberMobileBind($userMobile)
{
$user = $this->getCurrentUser();
if ($user->isLogin() && empty($user['verifiedMobile'])) {
$this->getUserService()->changeMobile($user['id'], $userMobile);
}
return true;
}
protected function findCourseTeachers($courses)
{
if (!$courses) {
return [];
}
$userIds = [];
foreach ($courses as $key => $course) {
$userIds = array_merge($userIds, $course['teacherIds']);
}
return $this->getUserService()->findUsersByIds($userIds);
}
/**
* @return UserService
*/
protected function getUserService()
{
return $this->createService('User:UserService');
}
/**
* @return TagService
*/
protected function getTagService()
{
return $this->createService('Taxonomy:TagService');
}
/**
* @return CourseSetService
*/
protected function getCourseSetService()
{
return $this->createService('Course:CourseSetService');
}
/**
* @return CourseService
*/
protected function getCourseService()
{
return $this->getBiz()->service('Course:CourseService');
}
/**
* @return SettingService
*/
protected function getSettingService()
{
return $this->createService('System:SettingService');
}
/**
* @return UploadFileService
*/
protected function getUploadFileService()
{
return $this->createService('File:UploadFileService');
}
/**
* @return TokenService
*/
protected function getTokenService()
{
return $this->createService('User:TokenService');
}
/**
* @return ThreadService
*/
protected function getThreadService()
{
return $this->createService('Thread:ThreadService');
}
/**
* @return MaterialService
*/
protected function getMaterialService()
{
return $this->createService('Course:MaterialService');
}
/**
* @return AuthService
*/
protected function getAuthService()
{
return $this->createService('User:AuthService');
}
/**
* @return OpenCourseRecommendedService
*/
protected function getOpenCourseRecommendedService()
{
return $this->createService('OpenCourse:OpenCourseRecommendedService');
}
protected function getLiveReplayService()
{
return $this->createService('Course:LiveReplayService');
}
/**
* @return FavoriteService
*/
protected function getFavoriteService()
{
return $this->createService('Favorite:FavoriteService');
}
/**
* @return GoodsService
*/
protected function getGoodsService()
{
return $this->createService('Goods:GoodsService');
}
}