src/AppBundle/Twig/WebExtension.php line 630

Open in your IDE?
  1. <?php
  2. namespace AppBundle\Twig;
  3. use ApiBundle\Api\Util\AssetHelper;
  4. use AppBundle\Common\ArrayToolkit;
  5. use AppBundle\Common\ConvertIpToolkit;
  6. use AppBundle\Common\DeviceToolkit;
  7. use AppBundle\Common\ExtensionManager;
  8. use AppBundle\Common\FileToolkit;
  9. use AppBundle\Common\LoginToolkit;
  10. use AppBundle\Common\MathToolkit;
  11. use AppBundle\Common\NumberToolkit;
  12. use AppBundle\Common\PluginVersionToolkit;
  13. use AppBundle\Common\SimpleValidator;
  14. use AppBundle\Component\DeviceDetector\DeviceDetectorAdapter;
  15. use AppBundle\Component\ShareSdk\WeixinShare;
  16. use AppBundle\Util\CategoryBuilder;
  17. use AppBundle\Util\CdnUrl;
  18. use AppBundle\Util\UploadToken;
  19. use Biz\Account\Service\AccountProxyService;
  20. use Biz\CloudPlatform\CloudAPIFactory;
  21. use Biz\CloudPlatform\Service\ResourceFacadeService;
  22. use Biz\Lms\Service\LmsService;
  23. use Biz\Lms\util\KnowledgeUrlBuilder;
  24. use Biz\ReviewCenter\Service\ReviewCenterService;
  25. use Biz\Taxonomy\Service\TagService;
  26. use Biz\User\Service\TokenService;
  27. use Codeages\Biz\Framework\Context\Biz;
  28. use Codeages\Biz\Framework\Service\Exception\ServiceException;
  29. use Common\CloudSchoolLevelConstant;
  30. use Common\CloudSchoolLevelHelper;
  31. use CorporateTrainingBundle\Biz\User\Service\UserService;
  32. use Symfony\Component\DependencyInjection\ContainerInterface;
  33. use Topxia\Service\Common\ServiceKernel;
  34. use Twig\Extension\AbstractExtension;
  35. class WebExtension extends AbstractExtension
  36. {
  37.     /**
  38.      * @var ContainerInterface
  39.      */
  40.     protected $container;
  41.     /**
  42.      * @var Biz
  43.      */
  44.     protected $biz;
  45.     protected $pageScripts;
  46.     protected $locale;
  47.     protected $defaultCloudSdkHost;
  48.     public function __construct($containerBiz $biz)
  49.     {
  50.         $this->container $container;
  51.         $this->biz $biz;
  52.     }
  53.     public function getFilters()
  54.     {
  55.         return [
  56.             new \Twig\TwigFilter('smart_time', [$this'smarttimeFilter']),
  57.             new \Twig\TwigFilter('date_format', [$this'dateformatFilter']),
  58.             new \Twig\TwigFilter('time_range', [$this'timeRangeFilter']),
  59.             new \Twig\TwigFilter('time_diff', [$this'timeDiffFilter']),
  60.             new \Twig\TwigFilter('remain_time', [$this'remainTimeFilter']),
  61.             new \Twig\TwigFilter('time_formatter', [$this'timeFormatterFilter']),
  62.             new \Twig\TwigFilter('location_text', [$this'locationTextFilter']),
  63.             new \Twig\TwigFilter('tags_html', [$this'tagsHtmlFilter'], ['is_safe' => ['html']]),
  64.             new \Twig\TwigFilter('file_size', [$this'fileSizeFilter']),
  65.             new \Twig\TwigFilter('plain_text', [$this'plainTextFilter'], ['is_safe' => ['html']]),
  66.             new \Twig\TwigFilter('sub_text', [$this'subTextFilter'], ['is_safe' => ['html']]),
  67.             new \Twig\TwigFilter('mb_substr', [$this'mb_substr'], ['is_safe' => ['html']]),
  68.             new \Twig\TwigFilter('duration', [$this'durationFilter']),
  69.             new \Twig\TwigFilter('duration_text', [$this'durationTextFilter']),
  70.             new \Twig\TwigFilter('format_ms_hms', [$this'formatMsToHmsFilter']),
  71.             new \Twig\TwigFilter('tags_join', [$this'tagsJoinFilter']),
  72.             new \Twig\TwigFilter('navigation_url', [$this'navigationUrlFilter']),
  73.             new \Twig\TwigFilter('chr', [$this'chrFilter']),
  74.             new \Twig\TwigFilter('bbCode2Html', [$this'bbCode2HtmlFilter']),
  75.             new \Twig\TwigFilter('score_text', [$this'scoreTextFilter']),
  76.             new \Twig\TwigFilter('simple_template', [$this'simpleTemplateFilter']),
  77.             new \Twig\TwigFilter('fill_question_stem_text', [$this'fillQuestionStemTextFilter']),
  78.             new \Twig\TwigFilter('fill_question_stem_html', [$this'fillQuestionStemHtmlFilter']),
  79.             new \Twig\TwigFilter('get_course_id', [$this'getCourseidFilter']),
  80.             new \Twig\TwigFilter('purify_html', [$this'getPurifyHtml']),
  81.             new \Twig\TwigFilter('purify_and_trim_html', [$this'getPurifyAndTrimHtml']),
  82.             new \Twig\TwigFilter('file_type', [$this'getFileType']),
  83.             new \Twig\TwigFilter('at', [$this'atFilter']),
  84.             new \Twig\TwigFilter('copyright_less', [$this'removeCopyright']),
  85.             new \Twig\TwigFilter('array_merge', [$this'arrayMerge']),
  86.             new \Twig\TwigFilter('space2nbsp', [$this'spaceToNbsp']),
  87.             new \Twig\TwigFilter('number_to_human', [$this'numberFilter']),
  88.             new \Twig\TwigFilter('array_column', [$this'arrayColumn']),
  89.             new \Twig\TwigFilter('rename_locale', [$this'renameLocale']),
  90.             new \Twig\TwigFilter('cdn', [$this'cdn']),
  91.             new \Twig\TwigFilter('wrap', [$this'wrap']),
  92.             new \Twig\TwigFilter('convert_absolute_url', [$this'convertAbsoluteUrl']),
  93.             new \Twig\TwigFilter('days_of_week', [$this'daysOfWeek']),
  94.             new \Twig\TwigFilter('filter_keys', [$this'filterArrayKeys']),
  95.             new \Twig\TwigFilter('exam_score', [$this'examScore']),
  96.             new \Twig\TwigFilter('learn_time_format', [$this'formatLearnTime']),
  97.             new \Twig\TwigFilter('group_by', [$this'groupBy']),
  98.         ];
  99.     }
  100.     public function getFunctions()
  101.     {
  102.         return [
  103.             new \Twig\TwigFunction('theme_global_script', [$this'getThemeGlobalScript']),
  104.             new \Twig\TwigFunction('file_uri_parse', [$this'parseFileUri']),
  105.             // file_path 即将废弃,不要再使用
  106.             new \Twig\TwigFunction('file_path', [$this'getFilePath']),
  107.             // default_path 即将废弃,不要再使用
  108.             new \Twig\TwigFunction('default_path', [$this'getDefaultPath']),
  109.             // file_url 即将废弃,不要再使用
  110.             new \Twig\TwigFunction('file_url', [$this'getFileUrl']),
  111.             // system_default_path,即将废弃,不要再使用
  112.             new \Twig\TwigFunction('system_default_path', [$this'getSystemDefaultPath']),
  113.             new \Twig\TwigFunction('fileurl', [$this'getFurl']),
  114.             new \Twig\TwigFunction('filepath', [$this'getFpath']),
  115.             new \Twig\TwigFunction('lazy_img', [$this'makeLazyImg'], ['is_safe' => ['html']]),
  116.             new \Twig\TwigFunction('avatar_path', [$this'avatarPath']),
  117.             new \Twig\TwigFunction('object_load', [$this'loadObject']),
  118.             new \Twig\TwigFunction('setting', [$this'getSetting']),
  119.             new \Twig\TwigFunction('set_price', [$this'getSetPrice']),
  120.             new \Twig\TwigFunction('percent', [$this'calculatePercent']),
  121.             new \Twig\TwigFunction('rate_format', [$this'rateFormat']),
  122.             new \Twig\TwigFunction('category_choices', [$this'getCategoryChoices']),
  123.             new \Twig\TwigFunction('category_choices_with_category_empty', [$this'getCategoryChoicesWithCategoryEmpty']),
  124.             new \Twig\TwigFunction('upload_max_filesize', [$this'getUploadMaxFilesize']),
  125.             new \Twig\TwigFunction('js_paths', [$this'getJsPaths']),
  126.             new \Twig\TwigFunction('is_plugin_installed', [$this'isPluginInstalled']),
  127.             new \Twig\TwigFunction('plugin_version', [$this'getPluginVersion']),
  128.             new \Twig\TwigFunction('version_compare', [$this'versionCompare']),
  129.             new \Twig\TwigFunction('is_exist_in_subarray_by_id', [$this'isExistInSubArrayById']),
  130.             new \Twig\TwigFunction('context_value', [$this'getContextValue']),
  131.             new \Twig\TwigFunction('is_feature_enabled', [$this'isFeatureEnabled']),
  132.             new \Twig\TwigFunction('parameter', [$this'getParameter']),
  133.             new \Twig\TwigFunction('upload_token', [$this'makeUpoadToken']),
  134.             new \Twig\TwigFunction('countdown_time', [$this'getCountdownTime']),
  135.             new \Twig\TwigFunction('can_show_classroom', [$this'canShowClassRoom']),
  136.             new \Twig\TwigFunction('can_show_article_group', [$this'canShowArticleGroup']),
  137.             new \Twig\TwigFunction('can_show_article', [$this'canShowArticle']),
  138.             new \Twig\TwigFunction('can_show_mobile_app', [$this'canShowMobileApp']),
  139.             new \Twig\TwigFunction('can_show_offline_activity', [$this'canShowOfflineActivity']),
  140.             //todo covertIP 要删除
  141.             new \Twig\TwigFunction('convertIP', [$this'getConvertIP']),
  142.             new \Twig\TwigFunction('convert_ip', [$this'getConvertIP']),
  143.             new \Twig\TwigFunction('new_convert_ip', [$this'getNewConverIP']),
  144.             new \Twig\TwigFunction('isHide', [$this'isHideThread']),
  145.             new \Twig\TwigFunction('user_coin_amount', [$this'userCoinAmount']),
  146.             new \Twig\TwigFunction('user_balance', [$this'getBalance']),
  147.             new \Twig\TwigFunction('blur_user_name', [$this'blurUserName']),
  148.             new \Twig\TwigFunction('blur_phone_number', [$this'blur_phone_number']),
  149.             new \Twig\TwigFunction('blur_idcard_number', [$this'blur_idcard_number']),
  150.             new \Twig\TwigFunction('blur_number', [$this'blur_number']),
  151.             new \Twig\TwigFunction('sub_str', [$this'subStr']),
  152.             new \Twig\TwigFunction('convert_encoding_sub_str', [$this'convertEncodingSubStr']),
  153.             new \Twig\TwigFunction('load_script', [$this'loadScript']),
  154.             new \Twig\TwigFunction('export_scripts', [$this'exportScripts']),
  155.             new \Twig\TwigFunction('order_payment', [$this'getOrderPayment']),
  156.             new \Twig\TwigFunction('classroom_permit', [$this'isPermitRole']),
  157.             new \Twig\TwigFunction('crontab_next_executed_time', [$this'getNextExecutedTime']),
  158.             new \Twig\TwigFunction('finger_print', [$this'getFingerprint']),
  159.             new \Twig\TwigFunction('get_parameters_from_url', [$this'getParametersFromUrl']),
  160.             new \Twig\TwigFunction('is_trial', [$this'isTrial']),
  161.             new \Twig\TwigFunction('timestamp', [$this'timestamp']),
  162.             new \Twig\TwigFunction('get_user_vip_level', [$this'getUserVipLevel']),
  163.             new \Twig\TwigFunction('is_without_network', [$this'isWithoutNetwork']),
  164.             new \Twig\TwigFunction('get_admin_roles', [$this'getAdminRoles']),
  165.             new \Twig\TwigFunction('render_notification', [$this'renderNotification']),
  166.             new \Twig\TwigFunction('route_exsit', [$this'routeExists']),
  167.             new \Twig\TwigFunction('is_micro_messenger', [$this'isMicroMessenger']),
  168.             new \Twig\TwigFunction('wx_js_sdk_config', [$this'weixinConfig']),
  169.             new \Twig\TwigFunction('plugin_update_notify', [$this'pluginUpdateNotify']),
  170.             new \Twig\TwigFunction('tag_equal', [$this'tagEqual']),
  171.             new \Twig\TwigFunction('get_tag', [$this'getTag']),
  172.             new \Twig\TwigFunction('array_index', [$this'arrayIndex']),
  173.             new \Twig\TwigFunction('cdn', [$this'getCdn']),
  174.             new \Twig\TwigFunction('is_show_mobile_page', [$this'isShowMobilePage']),
  175.             new \Twig\TwigFunction('is_mobile_client', [$this'isMobileClient']),
  176.             new \Twig\TwigFunction('is_allow_browse', [$this'isAllowBrowse']),
  177.             new \Twig\TwigFunction('is_ES_copyright', [$this'isESCopyright']),
  178.             new \Twig\TwigFunction('get_classroom_name', [$this'getClassroomName']),
  179.             new \Twig\TwigFunction('pop_reward_point_notify', [$this'popRewardPointNotify']),
  180.             new \Twig\TwigFunction('array_filter', [$this'arrayFilter']),
  181.             new \Twig\TwigFunction('base_path', [$this'basePath']),
  182.             new \Twig\TwigFunction('get_login_email_address', [$this'getLoginEmailAddress']),
  183.             new \Twig\TwigFunction('cloud_sdk_url', [$this'getCloudSdkUrl']),
  184.             new \Twig\TwigFunction('open_cloud_url', [$this'getOpenCloudUrl']),
  185.             new \Twig\TwigFunction('math_format', [$this'mathFormat']),
  186.             new \Twig\TwigFunction('parse_user_agent', [$this'parseUserAgent']),
  187.             new \Twig\TwigFunction('wechat_login_bind_enabled', [$this'isWechatLoginBind']),
  188.             new \Twig\TwigFunction('can_send_message', [$this'canSendMessage']),
  189.             new \Twig\TwigFunction('is_hidden_video_header', [$this'isHiddenVideoHeader']),
  190.             new \Twig\TwigFunction('arrays_key_convert', [$this'arraysKeyConvert']),
  191.             new \Twig\TwigFunction('make_local_media_file_token', [$this'makeLocalMediaFileToken']),
  192.             new \Twig\TwigFunction('online_advisory_auth_info', [$this'onlineAdvisoryAuthInfo']),
  193.             new \Twig\TwigFunction('question_html_filter', [$this'questionHtmlFilter']),
  194.             new \Twig\TwigFunction('uniqid', [$this'uniqid']),
  195.             new \Twig\TwigFunction('can_operate_question', [$this'canOperateQuestion']),
  196.             new \Twig\TwigFunction('sync_mode_dict', [$this'getSyncModeDict']),
  197.             new \Twig\TwigFunction('show_feedback_bar', [$this'showFeedbackBar']),
  198.             new \Twig\TwigFunction('is_saas', [$this'isSaaS']),
  199.             new \Twig\TwigFunction('member_deadline', [$this'handleDeadline']),
  200.             new \Twig\TwigFunction('get_course_market_url', [$this'getCourseMarketUrl']),
  201.             new \Twig\TwigFunction('org_msg', [$this'getOrgMsg']),
  202.             new \Twig\TwigFunction('get_category_name', [$this'getCategoryName']),
  203.             new \Twig\TwigFunction('get_category_ids_by_categories', [$this'getCategoryIdsByCategories']),
  204.             new \Twig\TwigFunction('get_review_pending_total_num', [$this'getReviewPendingTotalNum']),
  205.             new \Twig\TwigFunction('get_remember_me_deadline_range_text', [$this'getRememberMeDeadlineRangeText']),
  206.             new \Twig\TwigFunction('is_question_answered', [$this'isQuestionAnswered']),
  207.             new \Twig\TwigFunction('is_enabled_lms', [$this'isEnabledLms']),
  208.             new \Twig\TwigFunction('is_lms_version_supported', [$this'isLmsVersionSupported']),
  209.             new \Twig\TwigFunction('knowledge_href', [$this'knowledgeHref']),
  210.             new \Twig\TwigFunction('main_request_route', [$this'mainRequestRoute']),
  211.             new \Twig\TwigFunction('has_live_tasks', [$this'hasLiveTasks']),
  212.             new \Twig\TwigFunction('is_ai_coach_tenant', [$this'isAiCoachTenant'])
  213.         ];
  214.     }
  215.     public function getCourseMarketUrl()
  216.     {
  217.         return ServiceKernel::instance()->getParameter('security.course_market.url');
  218.     }
  219.     public function getOpenCloudUrl($path '')
  220.     {
  221.         return $this->createService('CloudPlatform:AppService')->getOpenCloudUrl($path);
  222.     }
  223.     public function parseUserAgent($userAgent)
  224.     {
  225.         $deviceDetector = new DeviceDetectorAdapter($userAgent);
  226.         return [
  227.             'device' => $deviceDetector->getDevice(),
  228.             'client' => $deviceDetector->getClient(),
  229.             'os' => $deviceDetector->getOs(),
  230.         ];
  231.     }
  232.     public function handleDeadline($deadline$issueDate 0)
  233.     {
  234.         if (== $deadline) {
  235.             return 0;
  236.         }
  237.         if (== $issueDate) {
  238.             $time time();
  239.         } else {
  240.             $time $issueDate;
  241.         }
  242.         if (($deadline $time) < 0) {
  243.             return -1;
  244.         }
  245.         return floor(($deadline $time) / 3600 24);
  246.     }
  247.     public function arrayFilter($data$filterName)
  248.     {
  249.         if (empty($data) || !is_array($data)) {
  250.             return [];
  251.         }
  252.         return array_filter($data, function ($value) use ($filterName) {
  253.             foreach ($filterName as $name) {
  254.                 if ('' === $value[$name]) {
  255.                     return false;
  256.                 }
  257.             }
  258.             return true;
  259.         });
  260.     }
  261.     public function getReviewPendingTotalNum()
  262.     {
  263.         $user $this->getUserService()->getCurrentUser();
  264.         $reviewPendingCount $this->getReviewCenterService()->getPendingTabbar($user['id']);
  265.         return array_sum(array_column($reviewPendingCount'pendingCount'));
  266.     }
  267.     public function isShowMobilePage()
  268.     {
  269.         $wapSetting $this->getSetting('wap', []);
  270.         if (empty($wapSetting['enabled'])) {
  271.             return false;
  272.         }
  273.         $pcVersion $this->container->get('request_stack')->getMainRequest()->cookies->get('PCVersion'0);
  274.         if ($pcVersion) {
  275.             return false;
  276.         }
  277.         return DeviceToolkit::isMobileClient();
  278.     }
  279.     public function isMobileClient()
  280.     {
  281.         return DeviceToolkit::isMobileClient();
  282.     }
  283.     public function isAllowBrowse()
  284.     {
  285.         $userAgent $this->container->get('request_stack')->getMainRequest()->headers->get('User-Agent');
  286.         $allowedBrowse = ['MicroMessenger''wxwork''DingTalk''Feishu'];
  287.         $allow false;
  288.         foreach ($allowedBrowse as $browse) {
  289.             if (false !== strpos($userAgent$browse)) {
  290.                 $allow true;
  291.                 break;
  292.             }
  293.         }
  294.         return $allow;
  295.     }
  296.     public function isESCopyright()
  297.     {
  298.         $copyright $this->getSetting('copyright');
  299.         $request $this->container->get('request_stack')->getMainRequest();
  300.         $host $request->getHttpHost();
  301.         if ($copyright) {
  302.             $result = !(
  303.                 isset($copyright['owned'])
  304.                 && isset($copyright['thirdCopyright'])
  305.                 && != $copyright['thirdCopyright']
  306.                 && isset($copyright['licenseDomains'])
  307.                 && in_array($hostexplode(';'$copyright['licenseDomains']))
  308.                 || (isset($copyright['thirdCopyright']) && == $copyright['thirdCopyright'])
  309.             );
  310.             return $result;
  311.         }
  312.         return true;
  313.     }
  314.     public function getClassroomName()
  315.     {
  316.         return $this->getSetting('classroom.name'$this->container->get('translator')->trans('site.default.classroom'));
  317.     }
  318.     public function tagEqual($tags$targetTagId$targetTagGroupId)
  319.     {
  320.         foreach ($tags as $groupId => $tagId) {
  321.             if ($groupId == $targetTagGroupId && $tagId == $targetTagId) {
  322.                 return true;
  323.             }
  324.         }
  325.         return false;
  326.     }
  327.     public function getTag($tagId)
  328.     {
  329.         if (empty($tagId)) {
  330.             return null;
  331.         }
  332.         return $this->getTagService()->getTag($tagId);
  333.     }
  334.     public function arrayIndex($array$key)
  335.     {
  336.         if (empty($array) || !is_array($array)) {
  337.             return [];
  338.         }
  339.         return ArrayToolkit::index($array$key);
  340.     }
  341.     public function timeFormatterFilter($time)
  342.     {
  343.         $hour floor($time 3600);
  344.         $minute floor(($time 3600) / 60);
  345.         $second $time 60;
  346.         if ($hour 0) {
  347.             if ($minute && $second 0) {
  348.                 return $this->trans('site.twig.extension.time_interval.hour_minute_second', [
  349.                     '%diff_hour%' => $hour,
  350.                     '%diff_minute%' => $minute,
  351.                     '%diff_second%' => $second,
  352.                 ]);
  353.             }
  354.             if ($minute 0) {
  355.                 return $this->trans('site.twig.extension.time_interval.hour_minute', [
  356.                     '%diff_hour%' => $hour,
  357.                     '%diff_minute%' => $minute,
  358.                 ]);
  359.             }
  360.             if ($second 0) {
  361.                 return $this->trans('site.twig.extension.time_interval.hour_second', [
  362.                     '%diff_hour%' => $hour,
  363.                     '%diff_second%' => $second,
  364.                 ]);
  365.             }
  366.             return $this->trans('site.twig.extension.time_interval.hour', [
  367.                 '%diff%' => $hour,
  368.             ]);
  369.         }
  370.         if ($minute 0) {
  371.             if ($second 0) {
  372.                 return $this->trans('site.twig.extension.time_interval.minute_second', [
  373.                     '%diff_minute%' => $minute,
  374.                     '%diff_second%' => $second,
  375.                 ]);
  376.             }
  377.             return $this->trans('site.twig.extension.time_interval.minute', [
  378.                 '%diff%' => $minute,
  379.             ]);
  380.         }
  381.         return $this->trans('site.twig.extension.time_interval.second', [
  382.             '%diff%' => $second,
  383.         ]);
  384.     }
  385.     public function pluginUpdateNotify()
  386.     {
  387.         $count $this->getAppService()->findAppCount();
  388.         $apps $this->getAppService()->findApps(0$count);
  389.         $apps array_filter($apps, function ($app) {
  390.             return 'EduSoho官方' == $app['developerName'];
  391.         });
  392.         $notifies array_reduce(
  393.             $apps,
  394.             function ($notifies$app) {
  395.                 if (!PluginVersionToolkit::dependencyVersion($app['code'], $app['version'])) {
  396.                     $notifies[$app['type']][] = $app['name'];
  397.                 } elseif ('TRAININGMAIN' !== $app['code'] && $app['protocol'] < 3) {
  398.                     $notifies[$app['type']][] = $app['name'];
  399.                 }
  400.                 return $notifies;
  401.             },
  402.             []
  403.         );
  404.         return $notifies;
  405.     }
  406.     public function getAdminRoles()
  407.     {
  408.         return $this->createService('Role:RoleService')->searchRoles([], 'created'01000);
  409.     }
  410.     public function getCdn($type 'default')
  411.     {
  412.         $cdn = new CdnUrl();
  413.         $cdnUrl $cdn->get($type);
  414.         return $cdnUrl;
  415.     }
  416.     public function cdn($content)
  417.     {
  418.         $cdn = new CdnUrl();
  419.         $cdnUrl $cdn->get('content');
  420.         if ($cdnUrl) {
  421.             $publicUrlPath $this->container->getParameter('topxia.upload.public_url_path');
  422.             $themeUrlPath $this->container->getParameter('topxia.web_themes_url_path');
  423.             $assetUrlPath $this->container->getParameter('topxia.web_assets_url_path');
  424.             $bundleUrlPath $this->container->getParameter('topxia.web_bundles_url_path');
  425.             $staticDistUrlPath $this->container->getParameter('front_end.web_static_dist_url_path');
  426.             preg_match_all('/<img[^>]*src=[\'"]?([^>\'"\s]*)[\'"]?[^>]*>/i'$content$imgs);
  427.             if ($imgs) {
  428.                 foreach ($imgs[1] as $img) {
  429.                     if (=== strpos($img$publicUrlPath)
  430.                         || === strpos($img$themeUrlPath)
  431.                         || === strpos($img$assetUrlPath)
  432.                         || === strpos($img$bundleUrlPath)
  433.                         || === strpos($img$staticDistUrlPath)) {
  434.                         $content str_replace('"'.$img'"'.$cdnUrl.$img$content);
  435.                     }
  436.                 }
  437.             }
  438.         }
  439.         return $content;
  440.     }
  441.     public function weixinConfig()
  442.     {
  443.         $weixinmob_enabled $this->getSetting('login_bind.weixinmob_enabled');
  444.         if (!(bool) $weixinmob_enabled) {
  445.             return null;
  446.         }
  447.         $jsApiTicket $this->createService('User:TokenService')->getTokenByType('jsapi.ticket');
  448.         $key $this->getSetting('login_bind.weixinmob_key');
  449.         $secret $this->getSetting('login_bind.weixinmob_secret');
  450.         if (empty($jsApiTicket)) {
  451.             $config = ['key' => $key'secret' => $secret];
  452.             $weixinshare = new WeixinShare($config);
  453.             $token $weixinshare->getJsApiTicket();
  454.             if (empty($token)) {
  455.                 return [];
  456.             }
  457.             $jsApiTicket $this->createService('User:TokenService')->makeToken(
  458.                 'jsapi.ticket',
  459.                 ['data' => $token'duration' => $token['expires_in']]
  460.             );
  461.         }
  462.         $config = [
  463.             'appId' => $key,
  464.             'timestamp' => time(),
  465.             'nonceStr' => uniqid($prefix 'edusoho'),
  466.             'jsApiList' => ['onMenuShareTimeline''onMenuShareAppMessage''onMenuShareQZone''onMenuShareQQ'],
  467.         ];
  468.         $jsapi_ticket $jsApiTicket['data']['ticket'];
  469.         $url $this->container->get('request_stack')->getMainRequest()->getUri();
  470.         $string 'jsapi_ticket='.$jsapi_ticket.'&noncestr='.$config['nonceStr'].'&timestamp='.$config['timestamp'].'&url='.$url;
  471.         $config['string'] = $string;
  472.         $config['signature'] = sha1($string);
  473.         return json_encode($config);
  474.     }
  475.     public function renderNotification($notification)
  476.     {
  477.         if ($notification) {
  478.             $manager ExtensionManager::instance();
  479.             $notification['message'] = $manager->renderNotification($notification);
  480.         }
  481.         return $notification;
  482.     }
  483.     public function routeExists($name)
  484.     {
  485.         $router $this->container->get('router');
  486.         return (null === $router->getRouteCollection()->get($name)) ? false true;
  487.     }
  488.     public function isWithoutNetwork()
  489.     {
  490.         $network $this->getSetting('developer.without_network'$default false);
  491.         return (bool) $network;
  492.     }
  493.     public function getUserVipLevel($userId)
  494.     {
  495.         return $this->createService('VipPlugin:Vip:VipService')->getMemberByUserId($userId);
  496.     }
  497.     public function getParametersFromUrl($url)
  498.     {
  499.         $BaseUrl parse_url($url);
  500.         if (isset($BaseUrl['query'])) {
  501.             if (strstr($BaseUrl['query'], '&')) {
  502.                 $parameter explode('&'$BaseUrl['query']);
  503.                 $parameters = [];
  504.                 foreach ($parameter as $key => $value) {
  505.                     $parameters[$key] = explode('='$value);
  506.                 }
  507.             } else {
  508.                 $parameter explode('='$BaseUrl['query']);
  509.                 $parameters = [];
  510.                 $parameters[0] = $parameter;
  511.             }
  512.         } else {
  513.             return null;
  514.         }
  515.         return $parameters;
  516.     }
  517.     public function spaceToNbsp($content)
  518.     {
  519.         $content str_replace(' ''&nbsp;'$content);
  520.         return $content;
  521.     }
  522.     public function isMicroMessenger()
  523.     {
  524.         return false !== strpos($this->container->get('request_stack')->getMainRequest()->headers->get('User-Agent'), 'MicroMessenger');
  525.     }
  526.     public function renameLocale($locale)
  527.     {
  528.         $locale strtolower($locale);
  529.         $locale str_replace('_''-'$locale);
  530.         return 'zh-cn' == $locale '' '-'.$locale;
  531.     }
  532.     public function getFingerprint()
  533.     {
  534.         $user $this->getUserService()->getCurrentUser();
  535.         if (!$user->isLogin()) {
  536.             return '';
  537.         }
  538.         $user $this->getUserService()->getUser($user['id']);
  539.         // @todo 如果配置用户的关键信息,这个方法存在信息泄漏风险,更换新播放器后解决这个问题。
  540.         $pattern $this->getSetting('magic.video_fingerprint');
  541.         if ($pattern) {
  542.             $fingerprint $this->parsePattern($pattern$user);
  543.         } else {
  544.             $request $this->container->get('request_stack')->getMainRequest();
  545.             $host $request->getHttpHost();
  546.             $fingerprint "{$host} {$user['nickname']}";
  547.         }
  548.         return $fingerprint;
  549.     }
  550.     public function popRewardPointNotify()
  551.     {
  552.         $session $this->container->get('session');
  553.         if (empty($session)) {
  554.             return '';
  555.         }
  556.         $message $session->get('Reward-Point-Notify');
  557.         $session->remove('Reward-Point-Notify');
  558.         return $message;
  559.     }
  560.     protected function parsePattern($pattern$user)
  561.     {
  562.         $profile $this->getUserService()->getUserProfile($user['id']);
  563.         $values array_merge($user$profile);
  564.         $values array_filter(
  565.             $values,
  566.             function ($value) {
  567.                 return !is_array($value);
  568.             }
  569.         );
  570.         return $this->simpleTemplateFilter($pattern$values);
  571.     }
  572.     public function subStr($text$start$length)
  573.     {
  574.         $text trim($text);
  575.         $length = (int) $length;
  576.         if (($length 0) && (mb_strlen($text) > $length)) {
  577.             $text mb_substr($text$start$length'UTF-8');
  578.         }
  579.         return $text;
  580.     }
  581.     public function convertEncodingSubStr($text$start$length)
  582.     {
  583.         $strlen = (strlen($text) + mb_strlen($text'UTF-8')) / 2;
  584.         if ($strlen <= $length) {
  585.             return $text;
  586.         }
  587.         $text mb_strcut(mb_convert_encoding($text'GBK''UTF-8'), 0$length'GBK');
  588.         return mb_convert_encoding($text'UTF-8''GBK').'...';
  589.     }
  590.     public function userCoinAmount($type$userId$startDateTime null$endDateTime null)
  591.     {
  592.         // 支付模块已移除,返回默认值
  593.         return 0;
  594.     }
  595.     public function getBalance($userId)
  596.     {
  597.         // 支付模块已移除,返回默认值
  598.         return ['amount' => 0'user_id' => $userId];
  599.     }
  600.     /**
  601.      * @return UserService
  602.      */
  603.     private function getUserService()
  604.     {
  605.         return $this->createService('User:UserService');
  606.     }
  607.     public function isExistInSubArrayById($currentTarget$targetArray)
  608.     {
  609.         foreach ($targetArray as $target) {
  610.             if ($currentTarget['id'] == $target['id']) {
  611.                 return true;
  612.             }
  613.         }
  614.         return false;
  615.     }
  616.     public function getThemeGlobalScript()
  617.     {
  618.         $theme $this->getSetting('theme.uri''default');
  619.         $filePath realpath(
  620.             $this->container->getParameter('kernel.root_dir')."/../web/themes/{$theme}/js/global-script.js"
  621.         );
  622.         if ($filePath) {
  623.             return 'theme/global-script';
  624.         }
  625.         return '';
  626.     }
  627.     public function isPluginInstalled($name)
  628.     {
  629.         return $this->container->get('kernel')->getPluginConfigurationManager()->isPluginInstalled($name);
  630.     }
  631.     public function canShowClassRoom()
  632.     {
  633.         return CloudSchoolLevelHelper::canShowClassRoom();
  634.     }
  635.     public function canShowArticle()
  636.     {
  637.         return CloudSchoolLevelHelper::canShowArticle();
  638.     }
  639.     public function canShowOfflineActivity()
  640.     {
  641.         return CloudSchoolLevelHelper::canShowOfflineActivity();
  642.     }
  643.     public function canShowMobileApp()
  644.     {
  645.         return CloudSchoolLevelHelper::canShowMobileApp();
  646.     }
  647.     public function canShowArticleGroup()
  648.     {
  649.         return CloudSchoolLevelHelper::canShowArticleGroup();
  650.     }
  651.     public function getPluginVersion($name)
  652.     {
  653.         $plugins $this->container->get('kernel')->getPlugins();
  654.         foreach ($plugins as $plugin) {
  655.             if (strtolower($plugin['code']) == strtolower($name)) {
  656.                 return $plugin['version'];
  657.             }
  658.         }
  659.         return null;
  660.     }
  661.     public function versionCompare($version1$version2$operator)
  662.     {
  663.         return version_compare($version1$version2$operator);
  664.     }
  665.     public function getJsPaths($excludedCdnResources = [])
  666.     {
  667.         $cdnUrl = new CdnUrl();
  668.         $basePath $cdnUrl->get();
  669.         if (empty($basePath)) {
  670.             $basePath $this->container->get('request_stack')->getMainRequest()->getBasePath();
  671.         }
  672.         $theme $this->getSetting('theme.uri''default');
  673.         $plugins $this->container->get('kernel')->getPlugins();
  674.         $names = [];
  675.         $newPluginNames = [];
  676.         foreach ($plugins as $plugin) {
  677.             if (is_array($plugin)) {
  678.                 if ('plugin' != $plugin['type']) {
  679.                     continue;
  680.                 }
  681.                 if (isset($plugin['protocol']) && == $plugin['protocol']) {
  682.                     $newPluginNames[] = $plugin['code'].'plugin';
  683.                 } else {
  684.                     $names[] = $plugin['code'];
  685.                 }
  686.             } else {
  687.                 $names[] = $plugin;
  688.             }
  689.         }
  690.         $names[] = 'customweb';
  691.         $names[] = 'customadmin';
  692.         $names[] = 'custom';
  693.         $names[] = 'topxiaweb';
  694.         $names[] = 'topxiaadmin';
  695.         $names[] = 'classroom';
  696.         $names[] = 'materiallib';
  697.         $names[] = 'sensitiveword';
  698.         $names[] = 'permission';
  699.         $names[] = 'org';
  700.         $names[] = 'corporatetraining';
  701.         $paths = [
  702.             'common' => 'common',
  703.             'theme' => "{$basePath}/themes/{$theme}/js",
  704.         ];
  705.         foreach ($names as $name) {
  706.             $name strtolower($name);
  707.             if (!empty($excludedCdnResources) && in_array($name$excludedCdnResources)) {
  708.                 $paths["{$name}bundle"] = "/bundles/{$name}/js";
  709.             } else {
  710.                 $paths["{$name}bundle"] = "{$basePath}/bundles/{$name}/js";
  711.             }
  712.         }
  713.         foreach ($newPluginNames as $newPluginName) {
  714.             $newPluginName strtolower($newPluginName);
  715.             if (!empty($excludedCdnResources) && in_array($newPluginName$excludedCdnResources)) {
  716.                 $paths["{$newPluginName}"] = "/bundles/{$newPluginName}/js";
  717.             } else {
  718.                 $paths["{$newPluginName}"] = "{$basePath}/bundles/{$newPluginName}/js";
  719.             }
  720.         }
  721.         // $paths['balloon-video-player'] = 'http://player-cdn.edusoho.net/balloon-video-player';
  722.         return $paths;
  723.     }
  724.     public function getContextValue($context$key)
  725.     {
  726.         $keys explode('.'$key);
  727.         $value $context;
  728.         foreach ($keys as $key) {
  729.             if (!isset($value[$key])) {
  730.                 throw new \InvalidArgumentException(sprintf('Key `%s` is not in context with %s'$keyimplode(array_keys($context), ', ')));
  731.             }
  732.             $value $value[$key];
  733.         }
  734.         return $value;
  735.     }
  736.     public function isFeatureEnabled($feature)
  737.     {
  738.         $features $this->container->hasParameter('enabled_features') ? $this->container->getParameter(
  739.             'enabled_features'
  740.         ) : [];
  741.         return in_array($feature$features);
  742.     }
  743.     public function getParameter($name$default null)
  744.     {
  745.         if (!$this->container->hasParameter($name)) {
  746.             return $default;
  747.         }
  748.         return $this->container->getParameter($name);
  749.     }
  750.     public function makeUpoadToken($group$type 'image'$duration 18000)
  751.     {
  752.         $maker = new UploadToken();
  753.         return $maker->make($group$type$duration);
  754.     }
  755.     public function getConvertIP($ip)
  756.     {
  757.         if (!empty($ip)) {
  758.             $location ConvertIpToolkit::convertIp($ip);
  759.             if ('INNA' === $location) {
  760.                 return '未知区域';
  761.             }
  762.             return $location;
  763.         }
  764.         return '';
  765.     }
  766.     public function getNewConverIP($ip)
  767.     {
  768.         if (!empty($ip)) {
  769.             $location ConvertIpToolkit::newConvertIp($ip);
  770.             if ('INNA' === $location) {
  771.                 return '未知区域';
  772.             }
  773.             $location array_filter($location);
  774.             return implode(' '$location);
  775.         }
  776.         return '';
  777.     }
  778.     public function dateformatFilter($time$format '')
  779.     {
  780.         if (empty($time)) {
  781.             return;
  782.         }
  783.         if (empty($format)) {
  784.             return date('Y-m-d H:i'$time);
  785.         }
  786.         return date($format$time);
  787.     }
  788.     public function smarttimeFilter($time)
  789.     {
  790.         $diff time() - $time;
  791.         if ($diff 0) {
  792.             return $this->trans('site.twig.extension.smarttime.future');
  793.         }
  794.         if (== $diff) {
  795.             return $this->trans('site.twig.extension.smarttime.hardly');
  796.         }
  797.         if ($diff 60) {
  798.             return $this->trans('site.twig.extension.smarttime.previous_second', ['%diff%' => $diff]);
  799.         }
  800.         if ($diff 3600) {
  801.             return $this->trans('site.twig.extension.smarttime.previous_minute', ['%diff%' => round($diff 60)]);
  802.         }
  803.         if ($diff 86400) {
  804.             return $this->trans('site.twig.extension.smarttime.previous_hour', ['%diff%' => round($diff 3600)]);
  805.         }
  806.         if ($diff 2592000) {
  807.             return $this->trans('site.twig.extension.smarttime.previous_day', ['%diff%' => round($diff 86400)]);
  808.         }
  809.         if ($diff 31536000) {
  810.             return date('m-d'$time);
  811.         }
  812.         return date('Y-m-d'$time);
  813.     }
  814.     public function remainTimeFilter($value$timeType '')
  815.     {
  816.         $remainTime = [];
  817.         $remain $value time();
  818.         if ($remain <= && empty($timeType)) {
  819.             return $remainTime['second'] = '0'.$this->trans('site.date.minute');
  820.         }
  821.         if ($remain <= 3600 && empty($timeType)) {
  822.             return $remainTime['minutes'] = round($remain 60).$this->trans('site.date.minute');
  823.         }
  824.         if ($remain 86400 && empty($timeType)) {
  825.             return $remainTime['hours'] = round($remain 3600).$this->trans('site.date.hour');
  826.         }
  827.         $remainTime['day'] = round(($remain $remain) / 86400).$this->trans('site.date.day');
  828.         if (!empty($timeType)) {
  829.             return $remainTime[$timeType];
  830.         } else {
  831.             return $remainTime['day'];
  832.         }
  833.     }
  834.     public function getCountdownTime($value)
  835.     {
  836.         $countdown = ['days' => 0'hours' => 0'minutes' => 0'seconds' => 0];
  837.         $remain $value time();
  838.         if ($remain <= 0) {
  839.             return $countdown;
  840.         }
  841.         $countdown['days'] = intval($remain 86400);
  842.         $remain $remain 86400 $countdown['days'];
  843.         $countdown['hours'] = intval($remain 3600);
  844.         $remain $remain 3600 $countdown['hours'];
  845.         $countdown['minutes'] = intval($remain 60);
  846.         $remain $remain 60 $countdown['minutes'];
  847.         $countdown['seconds'] = $remain;
  848.         return $countdown;
  849.     }
  850.     public function durationFilter($value)
  851.     {
  852.         $minutes intval($value 60);
  853.         $seconds $value $minutes 60;
  854.         return sprintf('%02d'$minutes).':'.sprintf('%02d'$seconds);
  855.     }
  856.     public function durationTextFilter($value)
  857.     {
  858.         $minutes intval($value 60);
  859.         $seconds $value $minutes 60;
  860.         if (=== $minutes) {
  861.             return $seconds.$this->trans('site.date.second');
  862.         }
  863.         return $this->trans('site.twig.extension.time_interval.minute_second', ['%diff_minute%' => $minutes'%diff_second%' => $seconds]);
  864.     }
  865.     /**
  866.      * 毫秒转 HH:MM:SS 格式,用于 segment 视频展示名等
  867.      */
  868.     public function formatMsToHmsFilter($ms)
  869.     {
  870.         $ms = (int) $ms;
  871.         $s = (int) floor($ms 1000);
  872.         $h = (int) floor($s 3600);
  873.         $m = (int) floor(($s 3600) / 60);
  874.         $s $s 60;
  875.         return sprintf('%02d:%02d:%02d'$h$m$s);
  876.     }
  877.     public function timeRangeFilter($start$end)
  878.     {
  879.         $range date('Y-n-d H:i'$start).' - ';
  880.         if ($this->container->get('topxia.timemachine')->inSameDay($start$end)) {
  881.             $range .= date('H:i'$end);
  882.         } else {
  883.             $range .= date('Y年n月d日 H:i'$end);
  884.         }
  885.         return $range;
  886.     }
  887.     public function timeDiffFilter($endTime$diffDay 0$startTime '')
  888.     {
  889.         $endSecond strtotime(date('Y-m-d'$endTime));
  890.         $startSecond = empty($startTime) ? strtotime(date('Y-m-d'time())) : $startTime;
  891.         $diffDay round(($endSecond $startSecond) / 864000PHP_ROUND_HALF_DOWN); // 丢弃小数点
  892.         return $diffDay $diffDay 0;
  893.     }
  894.     public function tagsJoinFilter($tagIds)
  895.     {
  896.         if (empty($tagIds) || !is_array($tagIds)) {
  897.             return '';
  898.         }
  899.         $tags $this->createService('Taxonomy:TagService')->findTagsByIds($tagIds);
  900.         $names ArrayToolkit::column($tags'name');
  901.         return join($names',');
  902.     }
  903.     public function navigationUrlFilter($url)
  904.     {
  905.         $url = (string) $url;
  906.         if (strpos($url'://')) {
  907.             return $url;
  908.         }
  909.         if (!empty($url[0]) && ('/' == $url[0])) {
  910.             return $url;
  911.         }
  912.         return $this->container->get('request_stack')->getMainRequest()->getBaseUrl().'/'.$url;
  913.     }
  914.     /**
  915.      *                            P -> 省全称,     p -> 省简称
  916.      *                            C -> 城市全称,    c -> 城市简称
  917.      *                            D -> 区全称,     d -> 区简称.
  918.      *
  919.      * @param [type] $districeId [description]
  920.      * @param string $format 格式,默认格式'P C D'
  921.      *
  922.      * @return [type] [description]
  923.      */
  924.     public function locationTextFilter($districeId$format 'P C D')
  925.     {
  926.         $text '';
  927.         $names $this->createService('Taxonomy:LocationService')->getLocationFullName($districeId);
  928.         $len strlen($format);
  929.         for ($i 0$i $len; ++$i) {
  930.             switch ($format[$i]) {
  931.                 case 'P':
  932.                     $text .= $names['province'];
  933.                     break;
  934.                 case 'p':
  935.                     $text .= $this->mb_trim($names['province'], '省');
  936.                     break;
  937.                 case 'C':
  938.                     $text .= $names['city'];
  939.                     break;
  940.                 case 'c':
  941.                     $text .= $this->mb_trim($names['city'], '市');
  942.                     break;
  943.                 case 'D':
  944.                 case 'd':
  945.                     $text .= $names['district'];
  946.                     break;
  947.                 default:
  948.                     $text .= $format[$i];
  949.                     break;
  950.             }
  951.         }
  952.         return $text;
  953.     }
  954.     public function tagsHtmlFilter($tags$class '')
  955.     {
  956.         $links = [];
  957.         $tags $this->createService('Taxonomy:TagService')->findTagsByIds($tags);
  958.         foreach ($tags as $tag) {
  959.             $url $this->container->get('router')->generate('course_explore', ['tagId' => $tag['id']]);
  960.             $links[] = "<a href=\"{$url}\" class=\"{$class}\">{$tag['name']}</a>";
  961.         }
  962.         return implode(' '$links);
  963.     }
  964.     public function parseFileUri($uri)
  965.     {
  966.         $kernel ServiceKernel::instance();
  967.         return $kernel->createService('Content:FileService')->parseFileUri($uri);
  968.     }
  969.     public function getFilePath($uri$default ''$absolute false)
  970.     {
  971.         $assets $this->container->get('assets.default_package_util');
  972.         $request $this->container->get('request_stack')->getMainRequest();
  973.         if (empty($uri)) {
  974.             $url $assets->getUrl('assets/img/default/'.$default);
  975.             // $url = $request->getBaseUrl() . '/assets/img/default/' . $default;
  976.             if ($absolute) {
  977.                 $url $request->getSchemeAndHttpHost().$url;
  978.             }
  979.             return $url;
  980.         }
  981.         if (false !== strpos($uri'http://')) {
  982.             return $uri;
  983.         }
  984.         $uri $this->parseFileUri($uri);
  985.         if ('public' == $uri['access']) {
  986.             $url rtrim($this->container->getParameter('topxia.upload.public_url_path'), ' /').'/'.$uri['path'];
  987.             $url ltrim($url' /');
  988.             $url $assets->getUrl($url);
  989.             if ($absolute) {
  990.                 $url $request->getSchemeAndHttpHost().$url;
  991.             }
  992.             return $url;
  993.         }
  994.     }
  995.     public function getDefaultPath($category$uri ''$size ''$absolute false)
  996.     {
  997.         $assets $this->container->get('assets.default_package_util');
  998.         $request $this->container->get('request_stack')->getMainRequest();
  999.         if (empty($uri)) {
  1000.             $publicUrlpath 'assets/img/default/';
  1001.             $url $assets->getUrl($publicUrlpath.$size.$category);
  1002.             $defaultSetting $this->createService('System:SettingService')->get('default', []);
  1003.             $key 'default'.ucfirst($category);
  1004.             $fileName $key.'FileName';
  1005.             if (array_key_exists($key$defaultSetting) && array_key_exists($fileName$defaultSetting)) {
  1006.                 if (== $defaultSetting[$key]) {
  1007.                     $url $assets->getUrl($publicUrlpath.$size.$defaultSetting[$fileName]);
  1008.                 }
  1009.             } elseif (array_key_exists($key$defaultSetting) && $defaultSetting[$key]) {
  1010.                 $uri $defaultSetting[$size.'Default'.ucfirst($category).'Uri'];
  1011.             } else {
  1012.                 return $url;
  1013.             }
  1014.             if ($absolute) {
  1015.                 $url $request->getSchemeAndHttpHost().$url;
  1016.             }
  1017.             return $url;
  1018.         }
  1019.         return $this->parseUri($uri$absolute);
  1020.     }
  1021.     public function avatarPath($user$type 'middle'$package 'user')
  1022.     {
  1023.         $avatar = !empty($user[$type.'Avatar']) ? $user[$type.'Avatar'] : null;
  1024.         if (empty($avatar)) {
  1025.             $avatar $this->getSetting('avatar.png');
  1026.         }
  1027.         return $this->getFpath($avatar'avatar.png'$package);
  1028.     }
  1029.     private function parseUri($uri$absolute false$package 'content')
  1030.     {
  1031.         if (false !== strpos($uri'http://') || false !== strpos($uri'https://')) {
  1032.             return $uri;
  1033.         }
  1034.         $assets $this->container->get('assets.default_package_util');
  1035.         $request $this->container->get('request_stack')->getMainRequest();
  1036.         if (strpos($uri'://')) {
  1037.             $uri $this->parseFileUri($uri);
  1038.             $url '';
  1039.             if ('public' == $uri['access']) {
  1040.                 $url $uri['path'];
  1041.             }
  1042.             if ('themes' == $uri['access']) {
  1043.                 return $this->addHost('/'.$uri['access'].'/'.$uri['path'], $absolute$package);
  1044.             }
  1045.         } else {
  1046.             $url $uri;
  1047.         }
  1048.         $url rtrim($this->container->getParameter('topxia.upload.public_url_path'), ' /').'/'.$url;
  1049.         return $this->addHost($url$absolute$package);
  1050.     }
  1051.     public function getSystemDefaultPath($defaultKey$absolute false)
  1052.     {
  1053.         $assets $this->container->get('assets.default_package_util');
  1054.         $defaultSetting $this->getSetting('default', []);
  1055.         if (array_key_exists($defaultKey$defaultSetting)
  1056.             && $defaultSetting[$defaultKey]
  1057.         ) {
  1058.             $path $defaultSetting[$defaultKey];
  1059.             return $this->parseUri($path$absolute);
  1060.         } else {
  1061.             $path $assets->getUrl('assets/img/default/'.$defaultKey);
  1062.             return $this->addHost($path$absolute);
  1063.         }
  1064.     }
  1065.     public function makeLazyImg($src$class ''$alt ''$img 'lazyload_course.png')
  1066.     {
  1067.         $imgpath $path $this->container->get('assets.default_package_util')->getUrl('assets/img/default/'.$img);
  1068.         return sprintf('<img src="%s" alt="%s" class="%s" data-echo="%s" />'$imgpath$alt$class$src);
  1069.     }
  1070.     public function loadScript($js)
  1071.     {
  1072.         $js is_array($js) ? $js : [$js];
  1073.         if ($this->pageScripts) {
  1074.             $this->pageScripts array_merge($this->pageScripts$js);
  1075.         } else {
  1076.             $this->pageScripts $js;
  1077.         }
  1078.     }
  1079.     public function exportScripts()
  1080.     {
  1081.         if (empty($this->pageScripts)) {
  1082.             $this->pageScripts = [];
  1083.         }
  1084.         return array_values(array_unique($this->pageScripts));
  1085.     }
  1086.     public function getFileUrl($uri$default ''$absolute false)
  1087.     {
  1088.         $assets $this->container->get('assets.default_package_util');
  1089.         $request $this->container->get('request_stack')->getMainRequest();
  1090.         if (empty($uri)) {
  1091.             $url $assets->getUrl('assets/img/default/'.$default);
  1092.             if ($absolute) {
  1093.                 $url $request->getSchemeAndHttpHost().$url;
  1094.             }
  1095.             return $url;
  1096.         }
  1097.         $url rtrim($this->container->getParameter('topxia.upload.public_url_path'), ' /').'/'.$uri;
  1098.         $url ltrim($url' /');
  1099.         $url $assets->getUrl($url);
  1100.         if ($absolute) {
  1101.             $url $request->getSchemeAndHttpHost().$url;
  1102.         }
  1103.         return $url;
  1104.     }
  1105.     public function getFurl($path$defaultKey false$package 'content')
  1106.     {
  1107.         return $this->getPublicFilePath($path$defaultKeytrue$package);
  1108.     }
  1109.     public function getFpath($path$defaultKey false$package 'content')
  1110.     {
  1111.         return $this->getPublicFilePath($path$defaultKeyfalse$package);
  1112.     }
  1113.     private function getPublicFilePath($path$defaultKey false$absolute false$package 'content')
  1114.     {
  1115.         $assets $this->container->get('assets.default_package_util');
  1116.         if (empty($path)) {
  1117.             $defaultSetting $this->getSetting('default', []);
  1118.             if ('user_avatar.png' == $defaultKey) {
  1119.                 $defaultKey 'avatar.png';
  1120.             }
  1121.             if ((('course.png' == $defaultKey && array_key_exists(
  1122.                             'defaultCoursePicture',
  1123.                             $defaultSetting
  1124.                         ) && == $defaultSetting['defaultCoursePicture'])
  1125.                     || ('avatar.png' == $defaultKey && array_key_exists(
  1126.                             'defaultAvatar',
  1127.                             $defaultSetting
  1128.                         ) && == $defaultSetting['defaultAvatar']))
  1129.                 && (array_key_exists($defaultKey$defaultSetting)
  1130.                     && $defaultSetting[$defaultKey])
  1131.             ) {
  1132.                 $path $defaultSetting[$defaultKey];
  1133.                 return $this->parseUri($path$absolute$package);
  1134.             } else {
  1135.                 return $this->addHost('/assets/img/default/'.$defaultKey$absolute$package);
  1136.             }
  1137.         }
  1138.         return $this->parseUri($path$absolute$package);
  1139.     }
  1140.     private function addHost($path$absolute$package 'content')
  1141.     {
  1142.         $cdn = new CdnUrl();
  1143.         $cdnUrl $cdn->get($package);
  1144.         if ($cdnUrl) {
  1145.             $isSecure $this->container->get('request_stack')->getMainRequest()->isSecure();
  1146.             $protocal $isSecure 'https:' 'http:';
  1147.             $path $protocal.$cdnUrl.$path;
  1148.         } elseif ($absolute) {
  1149.             $request $this->container->get('request_stack')->getMainRequest();
  1150.             $path $request->getSchemeAndHttpHost().$path;
  1151.         }
  1152.         return $path;
  1153.     }
  1154.     public function basePath($package 'content')
  1155.     {
  1156.         $cdn = new CdnUrl();
  1157.         $cdnUrl $cdn->get($package);
  1158.         if ($cdnUrl) {
  1159.             $isSecure $this->container->get('request_stack')->getMainRequest()->isSecure();
  1160.             $protocal $isSecure 'https:' 'http:';
  1161.             $path $protocal.$cdnUrl;
  1162.         } else {
  1163.             $request $this->container->get('request_stack')->getMainRequest();
  1164.             $path $request->getSchemeAndHttpHost();
  1165.         }
  1166.         return $path;
  1167.     }
  1168.     public function fileSizeFilter($size)
  1169.     {
  1170.         $currentValue $currentUnit null;
  1171.         $unitExps = ['B' => 0'KB' => 1'MB' => 2'GB' => 3];
  1172.         foreach ($unitExps as $unit => $exp) {
  1173.             $divisor pow(1024$exp);
  1174.             $currentUnit $unit;
  1175.             $currentValue $size $divisor;
  1176.             if ($currentValue 1024) {
  1177.                 break;
  1178.             }
  1179.         }
  1180.         return sprintf('%.2f'$currentValue).$currentUnit;
  1181.     }
  1182.     public function numberFilter($number)
  1183.     {
  1184.         if ($number <= 1000) {
  1185.             return $number;
  1186.         }
  1187.         $currentValue $currentUnit null;
  1188.         $unitExps = ['千' => 3'万' => 4'亿' => 8];
  1189.         foreach ($unitExps as $unit => $exp) {
  1190.             $divisor pow(10$exp);
  1191.             $currentUnit $unit;
  1192.             $currentValue $number $divisor;
  1193.             if ($currentValue 10) {
  1194.                 break;
  1195.             }
  1196.         }
  1197.         return sprintf('%.0f'$currentValue).$currentUnit;
  1198.     }
  1199.     public function loadObject($type$id)
  1200.     {
  1201.         $kernel ServiceKernel::instance();
  1202.         switch ($type) {
  1203.             case 'user':
  1204.                 return $kernel->createService('User:UserService')->getUser($id);
  1205.             case 'category':
  1206.                 return $this->getCategoryService()->getCategory($id);
  1207.             case 'course':
  1208.                 return $kernel->createService('Course:CourseService')->getCourse($id);
  1209.             case 'file_group':
  1210.                 return $kernel->createService('Content:FileService')->getFileGroup($id);
  1211.             default:
  1212.                 return null;
  1213.         }
  1214.     }
  1215.     public function plainTextFilter($text$length null)
  1216.     {
  1217.         $text strip_tags($text);
  1218.         $text str_replace(["\n""\r""\t"], ''$text);
  1219.         $text str_replace('&nbsp;'' '$text);
  1220.         $text trim($text);
  1221.         $length = (int) $length;
  1222.         if (($length 0) && (mb_strlen($text'UTF-8') > $length)) {
  1223.             $text mb_substr($text0$length'UTF-8');
  1224.             $text .= '...';
  1225.         }
  1226.         return $text;
  1227.     }
  1228.     public function subTextFilter($text$length null)
  1229.     {
  1230.         $text strip_tags($text);
  1231.         $text str_replace(["\n""\r""\t"], ''$text);
  1232.         $text str_replace('&nbsp;'' '$text);
  1233.         $text trim($text);
  1234.         $length = (int) $length;
  1235.         if (($length 0) && (mb_strlen($text'utf-8') > $length)) {
  1236.             $text mb_substr($text0$length'UTF-8');
  1237.             $text .= '...';
  1238.         }
  1239.         return $text;
  1240.     }
  1241.     public function mb_substr($text$length null)
  1242.     {
  1243.         $text strip_tags($text);
  1244.         $text str_replace(["\n""\r""\t"], ''$text);
  1245.         $text str_replace('&nbsp;'' '$text);
  1246.         $text trim($text);
  1247.         $length = (int) $length;
  1248.         if (($length 0) && (mb_strlen($text'utf-8') > $length)) {
  1249.             $text mb_substr($text0$length'UTF-8');
  1250.         }
  1251.         return $text;
  1252.     }
  1253.     public function getFileType($fileName$string null)
  1254.     {
  1255.         $fileName explode('.'$fileName);
  1256.         if ($string) {
  1257.             $name strtolower($fileName[count($fileName) - 1]).$string;
  1258.         }
  1259.         return $name;
  1260.     }
  1261.     public function getOrgMsg($orgId)
  1262.     {
  1263.         $orgService $this->createService('CorporateTrainingBundle:Org:OrgService');
  1264.         $org $orgService->getOrg($orgId);
  1265.         return $org;
  1266.     }
  1267.     public function getCategoryName($categoryId)
  1268.     {
  1269.         $categoryService $this->getCategoryService();
  1270.         if (empty($categoryId)) {
  1271.             return '';
  1272.         }
  1273.         $category $categoryService->getCategory($categoryId);
  1274.         if (empty($category)) {
  1275.             return '';
  1276.         }
  1277.         return $category['name'];
  1278.     }
  1279.     public function getCategoryIdsByCategories(array $categories)
  1280.     {
  1281.         if (empty($categories)) {
  1282.             return [];
  1283.         }
  1284.         return array_column($categories'id');
  1285.     }
  1286.     public function chrFilter($index)
  1287.     {
  1288.         return chr($index);
  1289.     }
  1290.     public function isHideThread($id)
  1291.     {
  1292.         $need $this->createService('Group:ThreadService')->sumGoodsCoinsByThreadId($id);
  1293.         $thread $this->createService('Group:ThreadService')->getThread($id);
  1294.         $data explode('[/hide]'$thread['content']);
  1295.         foreach ($data as $key => $value) {
  1296.             $value ' '.$value;
  1297.             sscanf($value'%[^[][hide=reply]%[^$$]'$replyContent$replyHideContent);
  1298.             if ($replyHideContent) {
  1299.                 return true;
  1300.             }
  1301.         }
  1302.         if ($need) {
  1303.             return true;
  1304.         }
  1305.         return false;
  1306.     }
  1307.     public function bbCode2HtmlFilter($bbCode)
  1308.     {
  1309.         $ext $this;
  1310.         $bbCode preg_replace_callback(
  1311.             '/\[image\](.*?)\[\/image\]/i',
  1312.             function ($matches) use ($ext) {
  1313.                 $src $ext->getFileUrl($matches[1]);
  1314.                 return "<img src='{$src}' />";
  1315.             },
  1316.             $bbCode
  1317.         );
  1318.         $bbCode preg_replace_callback(
  1319.             '/\[audio.*?id="(\d+)"\](.*?)\[\/audio\]/i',
  1320.             function ($matches) {
  1321.                 return "<span class='audio-play-trigger' href='javascript:;' data-file-id=\"{$matches[1]}\" data-file-type=\"audio\"></span>";
  1322.             },
  1323.             $bbCode
  1324.         );
  1325.         return $bbCode;
  1326.     }
  1327.     public function scoreTextFilter($text)
  1328.     {
  1329.         $text number_format($text1'.''');
  1330.         if ((int) $text == $text) {
  1331.             return (string) (int) $text;
  1332.         }
  1333.         return $text;
  1334.     }
  1335.     public function simpleTemplateFilter($text$variables)
  1336.     {
  1337.         foreach ($variables as $key => $value) {
  1338.             $text str_replace('{{'.$key.'}}'$value$text);
  1339.         }
  1340.         return $text;
  1341.     }
  1342.     public function fillQuestionStemTextFilter($stem)
  1343.     {
  1344.         return preg_replace('/\[\[.+?\]\]+/''____'$stem);
  1345.     }
  1346.     public function fillQuestionStemHtmlFilter($stem)
  1347.     {
  1348.         $index 0;
  1349.         $stem preg_replace_callback(
  1350.             '/\[\[.+?\]\]+/',
  1351.             function ($matches) use (&$index) {
  1352.                 ++$index;
  1353.                 return "<span class='question-stem-fill-blank'>({$index})</span>";
  1354.             },
  1355.             $stem
  1356.         );
  1357.         return $stem;
  1358.     }
  1359.     public function getCourseidFilter($target)
  1360.     {
  1361.         $target explode('/'$target);
  1362.         $target explode('-'$target[0]);
  1363.         return $target[1];
  1364.     }
  1365.     public function getPurifyHtml($html$trusted false)
  1366.     {
  1367.         if (empty($html)) {
  1368.             return '';
  1369.         }
  1370.         $biz $this->container->get('biz');
  1371.         return $biz['html_helper']->purify($html$trusted);
  1372.     }
  1373.     public function atFilter($text$ats = [])
  1374.     {
  1375.         if (empty($ats) || !is_array($ats)) {
  1376.             return $text;
  1377.         }
  1378.         $router $this->container->get('router');
  1379.         foreach ($ats as $nickname => $userId) {
  1380.             $path $router->generate('user_show', ['id' => $userId]);
  1381.             $html "<a href=\"{$path}\" data-uid=\"{$userId}\" target=\"_blank\">@{$nickname}</a>";
  1382.             $text preg_replace("/@{$nickname}/ui"$html$text);
  1383.         }
  1384.         return $text;
  1385.     }
  1386.     public function removeCopyright($source)
  1387.     {
  1388.         if ($this->getSetting('copyright.owned'false)) {
  1389.             $source str_ireplace('edusoho'''$source);
  1390.         }
  1391.         return $source;
  1392.     }
  1393.     public function getSetting($name$default null)
  1394.     {
  1395.         $names explode('.'$name);
  1396.         $name array_shift($names);
  1397.         if (empty($name)) {
  1398.             return $default;
  1399.         }
  1400.         $value $this->createService('System:SettingService')->get($name);
  1401.         if (!isset($value)) {
  1402.             return $default;
  1403.         }
  1404.         if (empty($names)) {
  1405.             return $value;
  1406.         }
  1407.         $result $value;
  1408.         foreach ($names as $name) {
  1409.             if (!isset($result[$name])) {
  1410.                 return $default;
  1411.             }
  1412.             $result $result[$name];
  1413.         }
  1414.         return $result;
  1415.     }
  1416.     public function getOrderPayment($order$default null)
  1417.     {
  1418.         $coinSettings $this->createService('System:SettingService')->get('coin', []);
  1419.         if (!isset($coinSettings['price_type'])) {
  1420.             $coinSettings['price_type'] = 'RMB';
  1421.         }
  1422.         if (!isset($coinSettings['coin_enabled'])) {
  1423.             $coinSettings['coin_enabled'] = 0;
  1424.         }
  1425.         if (!= $coinSettings['coin_enabled'] || 'coin' != $coinSettings['price_type']) {
  1426.             if ($order['coinAmount'] > && == $order['amount']) {
  1427.                 $default '余额支付';
  1428.             } else {
  1429.                 $dictExtension $this->container->get('codeages_plugin.dict_twig_extension');
  1430.                 $default $dictExtension->getDictText('payment'$order['payment']);
  1431.             }
  1432.         }
  1433.         return $default;
  1434.     }
  1435.     public function isPermitRole($classroomId$permission$isStudentOrAuditor false)
  1436.     {
  1437.         $funcName 'can'.$permission.'Classroom';
  1438.         if ($isStudentOrAuditor) {
  1439.             return $this->createService('Classroom:ClassroomService')->$funcName($classroomId$isStudentOrAuditor);
  1440.         }
  1441.         return $this->createService('Classroom:ClassroomService')->$funcName($classroomId);
  1442.     }
  1443.     public function calculatePercent($number$total)
  1444.     {
  1445.         if (== $number || == $total) {
  1446.             return '0%';
  1447.         }
  1448.         if ($number >= $total) {
  1449.             return '100%';
  1450.         }
  1451.         return round($number $total 100).'%';
  1452.     }
  1453.     public function rateFormat($rate$precision 0)
  1454.     {
  1455.         return round($rate$precision);
  1456.     }
  1457.     public function arrayMerge($text$content)
  1458.     {
  1459.         return array_merge($text$content);
  1460.     }
  1461.     public function getSetPrice($price)
  1462.     {
  1463.         return NumberToolkit::roundUp($price);
  1464.     }
  1465.     public function getCategoryChoices($groupCode$isFilter false$indent ' ')
  1466.     {
  1467.         $builder = new CategoryBuilder();
  1468.         return $builder->buildChoices($groupCode$isFilter$indent);
  1469.     }
  1470.     public function getCategoryChoicesWithCategoryEmpty($groupName$isFilter false$indent ' ')
  1471.     {
  1472.         $builder = new CategoryBuilder();
  1473.         $choices $builder->buildChoices($groupName$isFilter$indent);
  1474.         $newChoices[-1] = '未分类';
  1475.         return $newChoices $choices;
  1476.     }
  1477.     public function getNextExecutedTime()
  1478.     {
  1479.         return $this->createService('Crontab:CrontabService')->getNextExcutedTime();
  1480.     }
  1481.     public function getUploadMaxFilesize($formated true)
  1482.     {
  1483.         $max FileToolkit::getMaxFilesize();
  1484.         if ($formated) {
  1485.             return FileToolkit::formatFileSize($max);
  1486.         }
  1487.         return $max;
  1488.     }
  1489.     public function isTrial()
  1490.     {
  1491.         if (file_exists($this->container->getParameter('kernel.root_dir').'/data/trial.lock')) {
  1492.             return true;
  1493.         }
  1494.         return false;
  1495.     }
  1496.     public function timestamp()
  1497.     {
  1498.         return time();
  1499.     }
  1500.     public function blurUserName($name)
  1501.     {
  1502.         return mb_substr($name01'UTF-8').'**';
  1503.     }
  1504.     public function blur_phone_number($phoneNum)
  1505.     {
  1506.         $head substr($phoneNum03);
  1507.         $tail substr($phoneNum, -44);
  1508.         return $head.'****'.$tail;
  1509.     }
  1510.     public function blur_idcard_number($idcardNum)
  1511.     {
  1512.         $head substr($idcardNum04);
  1513.         $tail substr($idcardNum, -22);
  1514.         return $head.'************'.$tail;
  1515.     }
  1516.     public function blur_number($string)
  1517.     {
  1518.         if (SimpleValidator::email($string)) {
  1519.             $head substr($string01);
  1520.             $tail substr($stringstrpos($string'@'));
  1521.             return $head.'***'.$tail;
  1522.         } elseif (SimpleValidator::mobile($string)) {
  1523.             $head substr($string03);
  1524.             $tail substr($string, -44);
  1525.             return $head.'****'.$tail;
  1526.         } elseif (SimpleValidator::bankCardId($string)) {
  1527.             $tail substr($string, -44);
  1528.             return '**** **** **** '.$tail;
  1529.         } elseif (SimpleValidator::idcard($string)) {
  1530.             $head substr($string04);
  1531.             $tail substr($string, -22);
  1532.             return $head.'************'.$tail;
  1533.         }
  1534.     }
  1535.     public function mathFormat($number$multiplicator)
  1536.     {
  1537.         $number *= $multiplicator;
  1538.         return $number;
  1539.     }
  1540.     protected function createService($alias)
  1541.     {
  1542.         return $this->biz->service($alias);
  1543.     }
  1544.     protected function getAppService()
  1545.     {
  1546.         return $this->createService('CloudPlatform:AppService');
  1547.     }
  1548.     public function getPurifyAndTrimHtml($html)
  1549.     {
  1550.         $html strip_tags($html'');
  1551.         return preg_replace("/(\s|\&nbsp\;| |\xc2\xa0)/"''$html);
  1552.     }
  1553.     public function arrayColumn($array$column)
  1554.     {
  1555.         return ArrayToolkit::column($array$column);
  1556.     }
  1557.     private function trans($key$parameters = [])
  1558.     {
  1559.         return $this->container->get('translator')->trans($key$parameters);
  1560.     }
  1561.     public function mb_trim($string$charlist '\\\\s'$ltrim true$rtrim true)
  1562.     {
  1563.         $bothEnds $ltrim && $rtrim;
  1564.         $charClassInner preg_replace(
  1565.             ['/[\^\-\]\\\]/S''/\\\{4}/S'],
  1566.             ['\\\\\\0''\\'],
  1567.             $charlist
  1568.         );
  1569.         $workHorse '['.$charClassInner.']+';
  1570.         $ltrim && $leftPattern '^'.$workHorse;
  1571.         $rtrim && $rightPattern $workHorse.'$';
  1572.         if ($bothEnds) {
  1573.             $patternMiddle $leftPattern.'|'.$rightPattern;
  1574.         } elseif ($ltrim) {
  1575.             $patternMiddle $leftPattern;
  1576.         } else {
  1577.             $patternMiddle $rightPattern;
  1578.         }
  1579.         return preg_replace("/$patternMiddle/usSD"''$string);
  1580.     }
  1581.     public function wrap($object$type)
  1582.     {
  1583.         return $this->container->get('web.wrapper')->handle($object$type);
  1584.     }
  1585.     public function convertAbsoluteUrl($html)
  1586.     {
  1587.         $html preg_replace_callback('/src=[\'\"]\/(.*?)[\'\"]/', function ($matches) {
  1588.             $cdn = new CdnUrl();
  1589.             $cdnUrl $cdn->get('content');
  1590.             if (!empty($cdnUrl)) {
  1591.                 $absoluteUrl AssetHelper::getScheme().':'.rtrim($cdnUrl'/').'/'.ltrim($matches[1], '/');
  1592.             } else {
  1593.                 $absoluteUrl AssetHelper::uriForPath('/'.ltrim($matches[1], '/'));
  1594.             }
  1595.             return "src=\"{$absoluteUrl}\"";
  1596.         }, $html);
  1597.         return $html;
  1598.     }
  1599.     public function getLoginEmailAddress($email)
  1600.     {
  1601.         $dress explode('@'$email);
  1602.         $dress strtolower($dress[1]);
  1603.         $emailAddressMap = [
  1604.             'gmail.com' => 'mail.google.com',
  1605.             'vip.qq.com' => 'mail.qq.com',
  1606.             'vip.163.com' => 'vip.163.com',
  1607.             'vip.sina.com' => 'mail.sina.com.cn',
  1608.             'foxmail.com' => 'mail.qq.com',
  1609.             'hotmail.com' => 'www.hotmail.com',
  1610.             '188.com' => 'www.188.com',
  1611.             '139.com' => 'mail.10086.cn',
  1612.             '126.com' => 'www.126.com',
  1613.             'yeah.net' => 'yeah.net',
  1614.         ];
  1615.         if (!empty($emailAddressMap[$dress])) {
  1616.             return 'http://'.$emailAddressMap[$dress];
  1617.         }
  1618.         return 'http://mail.'.$dress;
  1619.     }
  1620.     public function getCloudSdkUrl($type)
  1621.     {
  1622.         return $this->getResourceFacadeService()->getFrontPlaySDKPathByType($type);
  1623.     }
  1624.     public function isWechatLoginBind()
  1625.     {
  1626.         $wechat $this->isMicroMessenger();
  1627.         $loginBind $this->getSetting('login_bind');
  1628.         return $wechat && !empty($loginBind['enabled']) && !empty($loginBind['weixinmob_enabled']);
  1629.     }
  1630.     public function makeLocalMediaFileToken($file)
  1631.     {
  1632.         $token $this->makeToken('local.media'$file['id']);
  1633.         return $token['token'];
  1634.     }
  1635.     /**
  1636.      * 获取在线咨询授权信息
  1637.      *
  1638.      * @return array
  1639.      */
  1640.     public function onlineAdvisoryAuthInfo()
  1641.     {
  1642.         $info = [
  1643.             'isCustom' => 'none',
  1644.         ];
  1645.         try {
  1646.             $api CloudAPIFactory::create('root');
  1647.             $info $api->get('/me');
  1648.             $displayLevel = ['personal''basic''medium''advanced''gold''custom'];
  1649.             if (is_array($info) && isset($info['level']) && in_array($info['level'], $displayLevel)) {
  1650.                 $info['isCustom'] = ('custom' == $info['level']) ? '是' '否';
  1651.             } else {
  1652.                 $info['isCustom'] = 'none';
  1653.             }
  1654.         } catch (\RuntimeException $e) {
  1655.         }
  1656.         return $info;
  1657.     }
  1658.     public function getSyncModeDict()
  1659.     {
  1660.         $dict = [
  1661.             'closed' => $this->trans('admin.sync-account-docking.btn.closed_btn'),
  1662.             'dingtalk' => $this->trans('admin.sync-account-docking.btn.dingtalk_btn'),
  1663.         ];
  1664.         if ($this->isPluginInstalled('WorkWechat')) {
  1665.             $dict['work_wechat'] = $this->trans('admin.sync-account-docking.btn.work_wechat_btn');
  1666.         }
  1667.         if ($this->isPluginInstalled('FeiShu')) {
  1668.             $dict['feishu'] = $this->trans('admin.sync-account-docking.btn.feishu_btn');
  1669.         }
  1670.         if ($this->isPluginInstalled('LDAP')) {
  1671.             $dict['LDAP'] = 'LDAP';
  1672.         }
  1673.         return $dict;
  1674.     }
  1675.     protected function makeToken($type$fileId$context = [])
  1676.     {
  1677.         $times = ('local.media' == $type) ? 100 10;
  1678.         $duration = ('local.media' == $type) ? 7200 3600;
  1679.         $fields = [
  1680.             'data' => [
  1681.                 'id' => $fileId,
  1682.             ],
  1683.             'times' => $times,
  1684.             'duration' => $duration,
  1685.             'userId' => $this->biz['user']['id'],
  1686.         ];
  1687.         if (isset($context['watchTimeLimit'])) {
  1688.             $fields['data']['watchTimeLimit'] = $context['watchTimeLimit'];
  1689.         }
  1690.         if (isset($context['hideBeginning'])) {
  1691.             $fields['data']['hideBeginning'] = $context['hideBeginning'];
  1692.         }
  1693.         return $this->getTokenService()->makeToken($type$fields);
  1694.     }
  1695.     public function isHiddenVideoHeader($isHidden false)
  1696.     {
  1697.         $storage $this->getSetting('storage');
  1698.         if (!empty($storage) && array_key_exists('video_header'$storage) && $storage['video_header'] && !$isHidden) {
  1699.             return false;
  1700.         }
  1701.         return true;
  1702.     }
  1703.     public function canSendMessage($userId)
  1704.     {
  1705.         $user $this->biz['user'];
  1706.         if (!$user->isLogin()) {
  1707.             return false;
  1708.         }
  1709.         if (in_array('ROLE_ADMIN'$user['roles']) || $user->isSuperAdmin()) {
  1710.             return true;
  1711.         }
  1712.         $toUser $this->getUserService()->getUser($userId);
  1713.         if ($user['id'] == $toUser['id']) {
  1714.             return false;
  1715.         }
  1716.         if (in_array('ROLE_ADMIN'$toUser['roles']) || in_array('ROLE_SUPER_ADMIN'$toUser['roles'])) {
  1717.             return true;
  1718.         }
  1719.         $messageSetting $this->getSetting('message', []);
  1720.         if (empty($messageSetting['teacherToStudent']) && $this->isTeacher($user['roles']) && $this->isOnlyStudent($toUser['roles'])) {
  1721.             return false;
  1722.         }
  1723.         if (empty($messageSetting['studentToStudent']) && $this->isOnlyStudent($user['roles']) && $this->isOnlyStudent($toUser['roles'])) {
  1724.             return false;
  1725.         }
  1726.         if (empty($messageSetting['studentToTeacher']) && $this->isOnlyStudent($user['roles']) && $this->isTeacher($toUser['roles'])) {
  1727.             return false;
  1728.         }
  1729.         return true;
  1730.     }
  1731.     public function daysOfWeek($day)
  1732.     {
  1733.         $weekName = ['日''一''二''三''四''五''六'];
  1734.         if (isset($weekName[$day])) {
  1735.             return $weekName[$day];
  1736.         }
  1737.         return false;
  1738.     }
  1739.     public function filterArrayKeys($array$keys)
  1740.     {
  1741.         foreach ($keys as $key) {
  1742.             unset($array[$key]);
  1743.         }
  1744.         return $array;
  1745.     }
  1746.     public function examScore($score)
  1747.     {
  1748.         return $score $score 0;
  1749.     }
  1750.     public function formatLearnTime($learnTime)
  1751.     {
  1752.         if ($learnTime 3600) {
  1753.             return round($learnTime 60).$this->trans('site.data.minute');
  1754.         }
  1755.         return round($learnTime 36001).$this->trans('site.date.hour');
  1756.     }
  1757.     public function groupBy($array$key)
  1758.     {
  1759.         return ArrayToolkit::group($array$key);
  1760.     }
  1761.     private function isTeacher($roles)
  1762.     {
  1763.         return in_array('ROLE_TEACHER'$roles);
  1764.     }
  1765.     private function isOnlyStudent($roles)
  1766.     {
  1767.         return in_array('ROLE_USER'$roles) && !in_array('ROLE_TEACHER'$roles) && !in_array('ROLE_ADMIN'$roles) && !in_array('ROLE_SUPER_ADMIN'$roles);
  1768.     }
  1769.     public function arraysKeyConvert($arrays$beforeKey$afterKey)
  1770.     {
  1771.         foreach ($arrays as $key => $value) {
  1772.             if ($value == $beforeKey) {
  1773.                 $arrays[$key][$afterKey] = $arrays[$key][$beforeKey];
  1774.                 unset($arrays[$key][$beforeKey]);
  1775.             }
  1776.         }
  1777.         return $arrays;
  1778.     }
  1779.     public function questionHtmlFilter($html$allowed '')
  1780.     {
  1781.         if (!isset($html)) {
  1782.             return '';
  1783.         }
  1784.         $html preg_replace('/(<img .*?src=")(.*?)(".*?>)/is''[图片]'$html);
  1785.         $security $this->getSettingService()->get('security');
  1786.         if (!empty($security['safe_iframe_domains'])) {
  1787.             $safeDomains $security['safe_iframe_domains'];
  1788.         } else {
  1789.             $safeDomains = [];
  1790.         }
  1791.         $config = [
  1792.             'cacheDir' => $this->biz['cache_directory'].'/htmlpurifier',
  1793.             'safeIframeDomains' => $safeDomains,
  1794.         ];
  1795.         $this->warmUp($config['cacheDir']);
  1796.         $htmlConfig = \HTMLPurifier_Config::createDefault();
  1797.         $htmlConfig->set('Cache.SerializerPath'$config['cacheDir']);
  1798.         $htmlConfig->set('HTML.Allowed'$allowed);
  1799.         $htmlpurifier = new \HTMLPurifier($htmlConfig);
  1800.         return $htmlpurifier->purify($html);
  1801.     }
  1802.     public function uniqid()
  1803.     {
  1804.         return MathToolkit::uniqid();
  1805.     }
  1806.     public function canOperateQuestion($user$question)
  1807.     {
  1808.         $currentUser $this->biz['user'];
  1809.         if ($currentUser->hasPermission('admin_question_gather_manage')) {
  1810.             return true;
  1811.         }
  1812.         if (in_array('ROLE_SUPER_ADMIN'$user['roles'])) {
  1813.             return true;
  1814.         }
  1815.         if (in_array('ROLE_TRAINING_ADMIN'$user['roles']) && $question['createdUserId'] === $user['id']) {
  1816.             return true;
  1817.         }
  1818.         return false;
  1819.     }
  1820.     public function showFeedbackBar($route)
  1821.     {
  1822.         if (in_array($route, [
  1823.             'course_teacher_evaluate_list',
  1824.             'project_plan_create',
  1825.             'project_plan_offline_course_homework_list',
  1826.             'survey_result_statistics',
  1827.         ])) {
  1828.             return true;
  1829.         }
  1830.         $allowedRoutes = [
  1831.             'course_set_manage',
  1832.             'course_manage',
  1833.             'classroom_manage',
  1834.             'offline_course_manage',
  1835.             'project_plan_study_data',
  1836.             'questionnaire_manage',
  1837.             'offline_activity_manage',
  1838.             'project_plan',
  1839.             'survey',
  1840.             'admin',
  1841.         ];
  1842.         foreach ($allowedRoutes as $allowedRoute) {
  1843.             if (!== stripos($route$allowedRoute)) {
  1844.                 continue;
  1845.             }
  1846.             if (!in_array($allowedRoute, ['project_plan''survey'])) {
  1847.                 return true;
  1848.             }
  1849.             if (false !== stripos($route'manage')) {
  1850.                 return true;
  1851.             }
  1852.         }
  1853.         if (false !== stripos($route'verify_list')) {
  1854.             return true;
  1855.         }
  1856.         return false;
  1857.     }
  1858.     public function isSaaS()
  1859.     {
  1860.         if ($this->isWithoutNetwork()) {
  1861.             return false;
  1862.         }
  1863.         $site $this->getSetting('site');
  1864.         if (empty($site['level']) || ($site['levelExpired'] ?? 0) < time()) {
  1865.             try {
  1866.                 $api CloudAPIFactory::create('root');
  1867.                 $info $api->get('/me');
  1868.             } catch (\RuntimeException $e) {
  1869.                 $info = [];
  1870.             }
  1871.             $site['level'] = $info['level'] ?? '';
  1872.             $site['levelExpired'] = time() + 7200;
  1873.             $this->getSettingService()->set('site'$site);
  1874.         }
  1875.         return in_array($this->getSetting('site.level'), $this->getSaaSLevels());
  1876.     }
  1877.     public function getSaaSLevels()
  1878.     {
  1879.         return CloudSchoolLevelConstant::SAAS_LEVELS;
  1880.     }
  1881.     public function getRememberMeDeadlineRangeText()
  1882.     {
  1883.         $rememberMeLifetime LoginToolkit::getRememberMeLifetime();
  1884.         if ($rememberMeLifetime 60 60) {
  1885.             return (int) ($rememberMeLifetime 60).$this->trans('site.data.minute');
  1886.         }
  1887.         if ($rememberMeLifetime 24 60 60) {
  1888.             return (int) ($rememberMeLifetime / (60 60)).$this->trans('site.date.hour');
  1889.         }
  1890.         return (int) ($rememberMeLifetime / (24 60 60)).$this->trans('site.date.day');
  1891.     }
  1892.     protected function warmUp($cacheDir)
  1893.     {
  1894.         if (!@mkdir($cacheDir0777true) && !is_dir($cacheDir)) {
  1895.             throw new ServiceException('mkdir cache dir error');
  1896.         }
  1897.         if (!is_writable($cacheDir)) {
  1898.             chmod($cacheDir0777);
  1899.         }
  1900.     }
  1901.     public function isQuestionAnswered($id)
  1902.     {
  1903.         $question $this->getQuestionService()->get($id);
  1904.         if (empty($question)) {
  1905.             return 0;
  1906.         }
  1907.         $count $this->getTestpaperService()->countItemResults(['questionId' => $question['id']]);
  1908.         if ($count) {
  1909.             return 1;
  1910.         }
  1911.         if ($this->isPluginInstalled('Exam')) {
  1912.             $count $this->getExamPluginTestPaperService()->countTestPaperItemResults(['questionId' => $question['id']]);
  1913.             if ($count) {
  1914.                 return 1;
  1915.             }
  1916.         }
  1917.         return 0;
  1918.     }
  1919.     public function isEnabledLms()
  1920.     {
  1921.         return $this->getLmsService()->isEnabledLms();
  1922.     }
  1923.     public function isLmsVersionSupported($version)
  1924.     {
  1925.         return $this->getLmsService()->isLmsVersionSupported($version);
  1926.     }
  1927.     public function knowledgeHref($knowledgeId$knowledgeBaseId)
  1928.     {
  1929.         return KnowledgeUrlBuilder::build($knowledgeId$knowledgeBaseIdDeviceToolkit::isMobileClient());
  1930.     }
  1931.     public function mainRequestRoute()
  1932.     {
  1933.         return $this->container->get('request_stack')->getMainRequest()->attributes->get('_route');
  1934.     }
  1935.     public function hasLiveTasks($courseId)
  1936.     {
  1937.         $tasks $this->getTaskService()->findTasksByCourseId($courseId);
  1938.         $hasLiveTasks ArrayToolkit::some($tasks, function ($task) {
  1939.             return 'live' === $task['type'];
  1940.         });
  1941.         return $hasLiveTasks;
  1942.     }
  1943.     /**
  1944.      * @return LmsService
  1945.      */
  1946.     protected function getLmsService()
  1947.     {
  1948.         return $this->createService('Lms:LmsService');
  1949.     }
  1950.     protected function getSettingService()
  1951.     {
  1952.         return $this->biz->service('System:SettingService');
  1953.     }
  1954.     /**
  1955.      * @return ResourceFacadeService
  1956.      */
  1957.     protected function getResourceFacadeService()
  1958.     {
  1959.         return $this->createService('CloudPlatform:ResourceFacadeService');
  1960.     }
  1961.     /**
  1962.      * @return TagService
  1963.      */
  1964.     protected function getTagService()
  1965.     {
  1966.         return $this->createService('Taxonomy:TagService');
  1967.     }
  1968.     /**
  1969.      * @return TokenService
  1970.      */
  1971.     protected function getTokenService()
  1972.     {
  1973.         return $this->createService('User:TokenService');
  1974.     }
  1975.     /**
  1976.      * @return mixed
  1977.      */
  1978.     public function getCategoryService()
  1979.     {
  1980.         return $this->createService('Taxonomy:CategoryService');
  1981.     }
  1982.     /**
  1983.      * @return ReviewCenterService
  1984.      */
  1985.     protected function getReviewCenterService()
  1986.     {
  1987.         return $this->createService('ReviewCenter:ReviewCenterService');
  1988.     }
  1989.     protected function getQuestionService()
  1990.     {
  1991.         return $this->createService('Question:QuestionService');
  1992.     }
  1993.     protected function getTestpaperService()
  1994.     {
  1995.         return $this->createService('Testpaper:TestpaperService');
  1996.     }
  1997.     protected function getExamPluginTestPaperService()
  1998.     {
  1999.         return $this->createService('ExamPlugin:TestPaper:TestPaperService');
  2000.     }
  2001.     /**
  2002.      * @return TaskService
  2003.      */
  2004.     protected function getTaskService()
  2005.     {
  2006.         return $this->createService('Task:TaskService');
  2007.     }
  2008.     /**
  2009.      * 判断当前租户是否为 AI 教练租户
  2010.      * 当 is_ai_coach_tenant.enabled = 1 时,隐藏 AI 教练不需要的相关功能入口
  2011.      *
  2012.      * @return bool
  2013.      */
  2014.     public function isAiCoachTenant()
  2015.     {
  2016.         try {
  2017.             return \Biz\System\AiCoachTenantSetting::isEnabled(
  2018.                 $this->getSettingService()->get('is_ai_coach_tenant', [])
  2019.             );
  2020.         } catch (\Exception $e) {
  2021.             return false;
  2022.         }
  2023.     }
  2024. }