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