Ошибка рендеринга шаблонизатора

Обнаружена проблема при обработке шаблона.

Сообщение об ошибке

An exception has been thrown during the compilation of a template ("Function statistic_top_counter not found").
Файл:
/home/lineagx/www/custom/plugins/brief_statistics/tpl/brief_statistic.html
Строка:
569
Тип:
Twig\Error\SyntaxError
Шаблон:
index.html
    1. {%extends 'struct.html'%}
    2. {%block title%}{{ phrase('personal account') }}{%endblock%}
    3. {%block content%}
    4. <div class="container-fluid">
    5. {%if config().enabled().isEnableEmulation()%}
    6. <div class="alert alert-dark" role="alert">
    7. {{ phrase('func_emulation_server_desc_enable')|raw }}
    8. </div>
    9. {%endif%}
    10. {%if get_session('super-user')%}
    11. <div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 col-sm-12 col-12">
    12. <div class="card bg-white border-0">
    13. <div class="alert custom-alert1 alert-secondary">
    14. <button type="button" class="btn-close ms-auto close_download_auth_panel" data-bs-dismiss="alert"
    15. aria-label="Close"><i class="bi bi-x"></i></button>
    16. <div class="text-center px-0 pb-0">
    17. <svg class="custom-alert-icon svg-secondary" xmlns="http://www.w3.org/2000/svg" height="1.5rem"
    18. viewBox="0 0 24 24" width="1.5rem" fill="#000000">
    19. <path d="M0 0h24v24H0z" fill="none" />
    20. <path
    21. d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
    22. </svg>
    23. <h5>Файл для авторизации</h5>
    24. <p class="">Вы успешно всё сделали, теперь можете загрузить файл, сохраните его, и на сайтах движка SphereWeb
    25. можете использовать для быстрой регистрации и авторизации, просто перенесите на странице авторизации данный
    26. файл.</p>
    27. <div class="">
    28. <button class="btn btn-sm btn-success m-0 download_file_auth">Нажмите чтоб загрузить файл</button>
    29. </div>
    30. </div>
    31. </div>
    32. </div>
    33. </div>
    34. <script>
    35. $('.download_file_auth').on('click', function () {
    36. var link = document.createElement('a');
    37. link.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent("{{get_session('super-user')}}");
    38. link.download = '{{getUser().getEmail()}} Auto Login.txt';
    39. document.body.appendChild(link);
    40. link.click();
    41. document.body.removeChild(link);
    42. });
    43. { { delete_session('super-user') } }
    44. </script>
    45. {%endif%}
    46. {# Если нет серверов, то показываем сообщение, что нужно зарегистрировать сервер #}
    47. {%if getUser().isAdmin() and getServerCount() == 0%}
    48. <div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 col-sm-12">
    49. <ul class="list-unstyled mb-0 notification-container">
    50. <li>
    51. <div class="card mb-3">
    52. <div class="d-flex p-2 border-bottom-0">
    53. <div class="d-sm-flex">
    54. <div class="">
    55. <svg class="me-4 bg-danger-transparent alt-notify" xmlns="http://www.w3.org/2000/svg"
    56. viewBox="0 0 24 24">
    57. <path fill="#ff9ea7"
    58. d="M20.057 22H3.943a3.023 3.023 0 0 1-2.618-4.534L9.382 3.511a3.023 3.023 0 0 1 5.236 0l8.057 13.955A3.023 3.023 0 0 1 20.057 22Z">
    59. </path>
    60. <circle cx="12" cy="17" r="1" fill="#dc3545"></circle>
    61. <path fill="#dc3545" d="M12 14a1 1 0 0 1-1-1V9a1 1 0 0 1 2 0v4a1 1 0 0 1-1 1Z"></path>
    62. </svg>
    63. </div>
    64. <div class="mt-0 text-start">
    65. <p class="fs-14 text-muted mb-0">{{phrase('no_registration_servers')}}<br>
    66. <a href="/admin/server/add/new" class=" text-primary d-inline-flex">{{phrase('register_server')}}</a>
    67. </p>
    68. </div>
    69. </div>
    70. </div>
    71. </div>
    72. </li>
    73. </ul>
    74. </div>
    75. {%endif%}
    76. {# Панель с предложением для неавторизованных пользователей #}
    77. {%if getUser().isAuth() == false%}
    78. <div class="row">
    79. <div class="col-xl-12">
    80. <div class="row">
    81. <div class="col-xl-4">
    82. <div class="card custom-card">
    83. <div class="card-body">
    84. <p class="card-text">{{phrase('log_in_crt_gm_acc')}}</p>
    85. <a href="/login" class="btn btn-success">{{phrase('authorization')}}</a>
    86. </div>
    87. </div>
    88. </div>
    89. <div class="col-xl-4">
    90. <div class="card custom-card">
    91. <div class="card-body">
    92. <p class="card-text">{{phrase('reg_crt_gm_accs')}}</p>
    93. <a href="/signup" class="btn btn-dark">{{phrase('registration')}}</a>
    94. </div>
    95. </div>
    96. </div>
    97. <div class="col-xl-4">
    98. <div class="card custom-card">
    99. <div class="card-body">
    100. <p class="card-text">{{phrase('forgot_pwd_recover_email')}}</p>
    101. <a href="/forget" class="btn btn-info">{{phrase(67)}}</a>
    102. </div>
    103. </div>
    104. </div>
    105. </div>
    106. </div>
    107. </div>
    108. {%endif%}
    109. {%if getUser().isAuth() and getServerCount() > 0%}
    110. <div class="row">
    111. <div class="col-xl-12">
    112. <div class="card custom-card">
    113. <div class="card-header d-flex justify-content-between align-items-center">
    114. <h5 class="card-title mb-0">{{phrase(43)}}</h5>
    115. </div>
    116. <div class="card-body">
    117. <div class="row mb-4">
    118. <div class="col-12">
    119. <form action="/registration/account" method="POST">
    120. <div class="row mb-3">
    121. <div class="col-12">
    122. <div
    123. class="alert alert-primary alert-dismissible fade show custom-alert-icon shadow-sm d-flex justify-content-between">
    124. {{ phrase(40) }} - {{ getServer().getName() }} x{{ getServer().getRateExp() }} ({{
    125. getServer().getChronicle() }})
    126. <span data-bs-toggle="modal" data-bs-target="#syncAccount">
    127. <label role="button" data-bs-toggle="tooltip" data-bs-placement="top"
    128. data-bs-original-title="{{phrase('sync_accounts_question_missing_account')}}">
    129. {{ phrase('Do you already have an account?') }}
    130. </label>
    131. </span>
    132. </div>
    133. </div>
    134. </div>
    135. <!-- Поля ввода в ряд -->
    136. <div class="row align-items-end mb-3">
    137. <!-- Поле логина -->
    138. <div class="col-md-4">
    139. <label for="accountRegistration" class="form-label">{{phrase(480)}}</label>
    140. <div class="input-group">
    141. {%set maxChars = config().registration().getMaximumNumberOfCharactersRegistrationAccount()%}
    142. {%set prefixEnabled = config().registration().getEnablePrefix()%}
    143. {%set prefixType = config().registration().getPrefixType()%}
    144. {%if prefixEnabled and prefixType == 'prefix'%}
    145. <button class="btn btn-light account_prefix" type="button">
    146. <span class="prefix-text">{{config().registration().genPrefix()}}</span>
    147. </button>
    148. {%endif%}
    149. <input type="text" class="form-control" name="login" id="accountRegistration"
    150. minlength="{{ config().registration().getMinimumNumberOfCharactersRegistrationAccount() }}"
    151. maxlength="{{ maxChars }}" placeholder="{{ phrase(480) }}" aria-label="Login input"
    152. aria-describedby="accountCharCounter" autocomplete="off">
    153. {%if prefixEnabled and prefixType == 'suffix'%}
    154. <button class="btn btn-light account_prefix" type="button">
    155. <span class="prefix-text">{{config().registration().genPrefix()}}</span>
    156. </button>
    157. {%endif%}
    158. </div>
    159. <div class="d-flex justify-content-between mt-1">
    160. <small class="form-text text-muted">
    161. {{ phrase('minMaxCharacters',
    162. config().registration().getMinimumNumberOfCharactersRegistrationAccount(),
    163. config().registration().getMaximumNumberOfCharactersRegistrationAccount()
    164. )}}
    165. </small>
    166. <small id="accountCharCounter" class="form-text text-muted">
    167. {{ phrase(312) }}: {{ maxChars }}
    168. </small>
    169. </div>
    170. </div>
    171. <!-- Поле пароля -->
    172. <div class="col-md-4">
    173. <label for="passwordRegistration" class="form-label">{{phrase('password')}}</label>
    174. <div class="input-group">
    175. <input type="password" minlength="4" maxlength="32" class="form-control" name="password"
    176. id="passwordRegistration" autocomplete="new-password" placeholder="{{phrase('password')}}">
    177. <button class="btn btn-link btn-sm toggle-password" type="button" id="togglePassword">
    178. <i class="bi bi-eye-fill"></i>
    179. </button>
    180. </div>
    181. <div class="form-check mt-1">
    182. <input checked class="form-check-input" type="checkbox" value="" name="password_hide"
    183. id="password_hide">
    184. <label class="form-check-label" for="password_hide" data-bs-toggle="tooltip"
    185. data-bs-placement="top" data-bs-original-title="{{phrase(483)}}">
    186. {{phrase(482)}}
    187. </label>
    188. </div>
    189. </div>
    190. <!-- Кнопки действий -->
    191. <div class="col-md-4">
    192. <div class="d-flex flex-column gap-2">
    193. <button type="submit" data-func="messageRegistrationAccount"
    194. class="btn btn-success shadow-success btn-wave waves-effect waves-light">
    195. {{phrase('reg_new_acc')}}
    196. </button>
    197. <div data-bs-toggle="tooltip" data-bs-placement="top"
    198. data-bs-original-title="{{phrase('sync_accounts_question_missing_account')}}">
    199. <span data-bs-toggle="modal" data-bs-target="#syncAccount"
    200. class="btn btn-info shadow-info btn-wave waves-effect waves-light w-100">
    201. {{phrase('bind_game_account')}}
    202. </span>
    203. </div>
    204. </div>
    205. </div>
    206. </div>
    207. <!-- Сообщение об ошибке -->
    208. <div class="row">
    209. <div class="col-12">
    210. <div id="registrationDataMessage" class="d-none">
    211. <span id="registrationMessage" class="badge border bg-danger-transparent rounded-1">
    212. {{ phrase(214) }}
    213. </span>
    214. </div>
    215. </div>
    216. </div>
    217. </form>
    218. </div>
    219. </div>
    220. <!-- Список аккаунтов -->
    221. <div class="row">
    222. <div class="col-12">
    223. <div
    224. class="alert alert-primary alert-dismissible fade show custom-alert-icon shadow-sm d-flex justify-content-between align-items-center mb-3">
    225. <div>{{ phrase(43) }}</div>
    226. <span>{{ getUser().getAccounts()|length }} / {{ config().other().getMaxAccount() }}</span>
    227. </div>
    228. {%if statusSphereServer() is same as(false)%}
    229. <div class="alert alert-danger alert-dismissible fade show" role="alert">
    230. <strong>{{phrase('fail_conn_sphereapi_srv')}}</strong>
    231. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
    232. </div>
    233. {%else%}
    234. <div class="table-responsive">
    235. <table class="table text-nowrap table-bordered table-sm" style="table-layout: fixed;">
    236. <thead>
    237. <tr>
    238. <th>{{phrase('account')}}</th>
    239. <th>{{phrase('password')}}</th>
    240. <th>{{phrase(203)}}</th>
    241. </tr>
    242. </thead>
    243. <tbody id="player_account_list">
    244. {%for account in getUser().getAccounts()%}
    245. <tr class="account-row">
    246. <td>{{ account.getAccount()|raw }}
    247. {%if config().other().getIsAllowDeleteAccount()%}
    248. <i data-account="{{account.getAccount()|raw}}" role="button"
    249. class="bi bi-person-dash deleteAccount" data-bs-toggle="tooltip" data-bs-placement="top"
    250. data-bs-title="{{phrase('account_deletion')}}"></i>
    251. {%endif%}
    252. </td>
    253. <td>
    254. <i role="button" class="fe fe-settings btn-change-password"
    255. data-account="{{account.getAccount()|raw}}" data-bs-toggle="modal"
    256. data-bs-effect="effect-slide-in-right" data-bs-target="#changepassword">
    257. {%if account.isPasswordHide()%}
    258. * * * * * *
    259. {%else%}
    260. {{ account.getPassword() }}
    261. {%endif%}
    262. </i>
    263. </td>
    264. <td class="text-nowrap" role="button">
    265. {%if account.getCharactersCount() > 0%}
    266. <div class="toggle-characters" data-account="{{account.getAccount()|raw}}">
    267. <div class="avatar-list-stacked">
    268. {%for i, character in account.getCharacters()%}
    269. <span class="avatar avatar-sm me-2">
    270. <img
    271. src="{{tempate}}/uploads/images/race/{{ sex(character.getSex()) }}/{{ get_class_race(character.getClassId()) }}.jpg"
    272. alt="img">
    273. </span>
    274. </a>
    275. {%endfor%}
    276. </div>
    277. </div>
    278. {%else%}
    279. <i class="bi bi-people ms-2 text-muted" data-bs-toggle="tooltip" data-bs-placement="top"
    280. data-bs-title="{{phrase('no_chars_acc')}}"></i>
    281. {%endif%}
    282. </td>
    283. </tr>
    284. {%if account.getCharactersCount() > 0%}
    285. <tr class="character-container character-row-{{account.getAccount()|raw}}" style="display: none;">
    286. <td colspan="3" class="p-0">
    287. <div class="p-2 bg-light">
    288. <div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 g-2">
    289. {%for i, character in account.getCharacters()%}
    290. <div class="col">
    291. <div class="card border h-100 overflow-hidden">
    292. <div class="card-body p-1 border-top-card border-top-danger rounded-0">
    293. <div class="d-flex align-items-start">
    294. <!-- Аватар персонажа -->
    295. <div class="flex-shrink-0 me-2">
    296. <img
    297. src="{{tempate}}/uploads/images/race/{{ sex(character.getSex()) }}/{{ get_class_race(character.getClassId()) }}.jpg"
    298. alt=""
    299. class="avatar avatar-xl img-thumbnail {%if character.getOnline()%}online{%else%}offline{%endif%}">
    300. </div>
    301. <!-- Информация о персонаже -->
    302. <div class="flex-grow-1 min-w-0">
    303. <h6 class="card-title mb-1 text-truncate">
    304. <span class="badge bg-dark text-white">{{character.getPlayerName()}}</span>
    305. </h6>
    306. <div class="d-flex flex-column" style="line-height: 1.1;">
    307. <small class="text-muted text-truncate mb-0">
    308. <i class="ri-building-line me-1"></i>Lv: {{character.getLevel()}}
    309. </small>
    310. <small class="text-muted text-truncate mb-0">
    311. <i class="ri-building-line me-1"></i>Class:
    312. {{get_class(character.getClassId())}}
    313. </small>
    314. <small class="text-muted text-truncate mb-0">
    315. <i class="ri-building-line me-1"></i>PvP / PK: {{character.getPvp()}} /
    316. {{character.getPk()}}
    317. </small>
    318. <small class="text-muted text-truncate mb-0">
    319. <i class="ri-building-line me-1"></i>Online Time:
    320. {{timeHasPassed(character.getTimeInGame(),true)}}
    321. </small>
    322. {%if character.getClanName()%}
    323. <small class="text-muted">
    324. <i class="ri-map-pin-line me-1"></i>Clan:
    325. <span class="d-inline-flex align-items-center">
    326. {{clan_icon(character.getClanCrest())|raw}}
    327. <span class="text-truncate d-inline-block"
    328. style="max-width: 120px; vertical-align: middle;">{{character.getClanName()}}</span>
    329. </span>
    330. </small>
    331. {%endif%}
    332. </div>
    333. </div>
    334. </div>
    335. <!-- Аккордеон с функциями -->
    336. <div class="accordion accordion-customicon1 accordions-items-seperate mt-2"
    337. id="accordion_open_list_{{character.getPlayerId()}}">
    338. <div class="accordion-item">
    339. <h2 class="accordion-header" id="open_list_{{character.getPlayerId()}}">
    340. <button class="accordion-button collapsed py-2 text-wrap" type="button"
    341. data-bs-toggle="collapse"
    342. data-bs-target="#collapse_open_list_{{character.getPlayerId()}}"
    343. aria-expanded="false"
    344. aria-controls="collapse_open_list_{{character.getPlayerId()}}">
    345. <span class="text-truncate">{{phrase('stuck_crit_err')}}</span>
    346. </button>
    347. </h2>
    348. <div id="collapse_open_list_{{character.getPlayerId()}}"
    349. class="accordion-collapse collapse"
    350. aria-labelledby="open_list_{{character.getPlayerId()}}"
    351. data-bs-parent="#accordion_open_list_{{character.getPlayerId()}}" style="">
    352. <div class="accordion-body py-2 text-wrap">
    353. <p class="small mb-2 text-break">{{phrase('emerg_send_char_town')}}</p>
    354. <hr class="my-2">
    355. <form method="post" action="/player/relocation">
    356. <div class="mb-2 form-check">
    357. <input name="account" type="hidden" value="{{account.getAccount()|raw}}">
    358. <input name="player" type="hidden" value="{{character.getPlayerName()}}">
    359. {%if getServer().isResetItemsToWarehouse() == true%}
    360. <input checked name="itemsToWarehouse" type="checkbox"
    361. class="form-check-input" id="check_{{character.getPlayerId()}}">
    362. <label class="form-check-label small text-wrap"
    363. for="check_{{character.getPlayerId()}}">{{phrase('send_items_wh')}}</label>
    364. {%endif%}
    365. </div>
    366. <button type="submit" class="btn btn-success btn-sm w-100 text-nowrap">
    367. <span class="d-inline-block text-truncate"
    368. style="max-width: 85%;">{{phrase(364)}}</span>
    369. <i class="ri-send-plane-fill ms-1"></i>
    370. </button>
    371. </form>
    372. </div>
    373. </div>
    374. </div>
    375. {%if getServer().isResetHWID() == true%}
    376. <div class="accordion-item">
    377. <h2 class="accordion-header" id="hwid_remove_{{character.getPlayerId()}}">
    378. <button class="accordion-button collapsed py-2 text-wrap" type="button"
    379. data-bs-toggle="collapse"
    380. data-bs-target="#collapse_hwid_remove_{{character.getPlayerId()}}"
    381. aria-expanded="false"
    382. aria-controls="collapse_hwid_remove_{{character.getPlayerId()}}">
    383. <span class="text-truncate">{{phrase('reset_hwid')}}</span>
    384. </button>
    385. </h2>
    386. <div id="collapse_hwid_remove_{{character.getPlayerId()}}"
    387. class="accordion-collapse collapse"
    388. aria-labelledby="open_list_{{character.getPlayerId()}}"
    389. data-bs-parent="#accordion_open_list_{{character.getPlayerId()}}" style="">
    390. <div class="accordion-body py-2 text-wrap">
    391. <p class="small mb-2 text-break">{{phrase('reset_hwid_emerg_fn')}}.</p>
    392. <button type="submit"
    393. class="btn btn-success btn-sm w-100 removeHWID text-nowrap"
    394. data-obj="{{character.getPlayerId()}}"
    395. data-account="{{account.getAccount()}}">
    396. <span class="d-inline-block text-truncate"
    397. style="max-width: 85%;">{{phrase('reset_hwid')}}</span>
    398. <i class="ri-lock-unlock-line ms-1"></i>
    399. </button>
    400. </div>
    401. </div>
    402. </div>
    403. {%endif%}
    404. </div>
    405. </div>
    406. </div>
    407. </div>
    408. {%endfor%}
    409. </div>
    410. </div>
    411. </td>
    412. </tr>
    413. {%endif%}
    414. {%endfor%}
    415. </tbody>
    416. </table>
    417. </div>
    418. {%endif%}
    419. </div>
    420. </div>
    421. </div>
    422. </div>
    423. </div>
    424. </div>
    425. {%if config().other().getIsAllowDeleteAccount()%}
    426. <div class="modal fade" id="deleteAccountModal" tabindex="-1" aria-labelledby="deleteAccountModalLabel"
    427. aria-hidden="true">
    428. <div class="modal-dialog modal-dialog-centered">
    429. <div class="modal-content">
    430. <div class="modal-header">
    431. <h5 class="modal-title" id="deleteAccountModalLabel">{{phrase('confirm_delete')}}</h5>
    432. <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
    433. </div>
    434. <div class="modal-body">
    435. {{phrase('delete_account_confirm')}} <span class="fw-bold" id="accountToDelete"></span>?
    436. <p class="font-13 text-muted">{{phrase('delete_account_desc')}}</p>
    437. </div>
    438. <div class="modal-footer">
    439. <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{phrase('no')}}</button>
    440. <button type="button" class="btn btn-danger" id="confirmDeleteAccount">{{phrase('yes')}}</button>
    441. </div>
    442. </div>
    443. </div>
    444. </div>
    445. <script>
    446. $(document).ready(function () {
    447. // Обработчик клика по иконке удаления аккаунта
    448. $(document).on('click', '.deleteAccount', function () {
    449. var accountName = $(this).data('account');
    450. $('#accountToDelete').text(accountName);
    451. $('#confirmDeleteAccount').data('account', accountName);
    452. $('#deleteAccountModal').modal('show');
    453. });
    454. // Обработчик подтверждения удаления
    455. $(document).on('click', '#confirmDeleteAccount', function () {
    456. var accountName = $(this).data('account');
    457. // Отправка AJAX запроса на удаление аккаунта
    458. AjaxSend('/player/account/delete', 'POST', {
    459. account: accountName
    460. }, true).then(function (response) {
    461. $('#deleteAccountModal').modal('hide');
    462. responseAnalysis(response);
    463. if (response.ok) {
    464. $('tr.account-row').has('i[data-account="' + accountName + '"]').remove();
    465. $('tr.character-row-' + accountName).remove();
    466. }
    467. }).catch(function (error) {
    468. $('#deleteAccountModal').modal('hide');
    469. });
    470. });
    471. });
    472. </script>
    473. {%endif%}
    474. <form action="/player/account/change/password" method="POST">
    475. <div class="modal fade" id="changepassword">
    476. <div class="modal-dialog modal-dialog-centered text-center" role="document">
    477. <div class="modal-content modal-content-demo">
    478. <div class="modal-header">
    479. <h6 class="modal-title">{{phrase(1)}}</h6>
    480. <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button>
    481. </div>
    482. <input name="login" type="hidden" class="account_new_change_password_input" value="">
    483. <div class="modal-body text-start">
    484. <h6 class="text-center">{{phrase('new_password')}} <span
    485. class="text-danger account_new_change_password">{{getUser().getName()}}</span></h6>
    486. <div class="form-floating">
    487. <input name="password" type="password" class="form-control" id="floatingPassword" placeholder="Password">
    488. <label for="floatingPassword">{{phrase('password')}}</label>
    489. </div>
    490. <div class="col-sm-10">
    491. <div class="form-check">
    492. <input name="password_hide" class="form-check-input" type="checkbox" id="hidePassword">
    493. <label class="form-check-label" for="hidePassword">
    494. {{phrase('lv_pwd_vis')}}
    495. </label>
    496. </div>
    497. </div>
    498. </div>
    499. <div class="modal-footer">
    500. <button type="button" class="btn btn-light" data-bs-dismiss="modal">{{phrase(80)}}</button>
    501. <button type="submit" class="btn btn-primary">{{phrase(89)}}</button>
    502. </div>
    503. </div>
    504. </div>
    505. </div>
    506. </form>
    507. {%endif%}
    508. {%for include_file in get_plugins_include("PLACE_IN_SPACE_MAIN_1")%}
    509. {%include include_file%}
    510. {%endfor%}
    511. {%if forum().isEnabled() and forum().showForumSphereMainPage()%}
    512. <div class="row">
    513. <div class="col-xxl-12 col-md-12">
    514. <div class="card">
    515. <div class="card-header">
    516. <h4 class="card-title fw-semibold">{{phrase('last_act_forum')}}</h4>
    517. </div>
    518. <div class="card-body pb-0">
    519. {%if forum().isNotError()%}
    520. <div class="list-group">
    521. {%if forum().getSort() == "threads"%}
    522. {%for forum in forum().lastThreads()%}
    523. <a href="{{ forum.thread_url }}" class="list-group-item list-group-item-action">
    524. <div class="d-flex align-items-center">
    525. <div>
    526. <span class="avatar cover-image avatar-sm bg-white text-default avatar-rounded me-2">
    527. <img src="{{ forum.avatar }}" alt="img" class="avatar-img">
    528. </span>
    529. </div>
    530. <div class="ms-2 w-100 d-flex justify-content-between align-items-center">
    531. <div>
    532. <span class="fw-semibold me-1">{{ forum.username }}</span>
    533. <span class="text-muted fs-12">:</span>
    534. <span class="text-truncate" >{{ forum.title }}</span>
    535. </div>
    536. <div class="text-muted fs-11 ms-3">{{ forum.last_post_date|date('h:m:s d.m.Y') }}</div>
    537. </div>
    538. </div>
    539. </a>
    540. {%endfor%}
    541. {%else%}
    542. {%for forum in forum().lastMessage()%}
    543. <a href="{{ forum.thread_url|default('javascript:void(0)') }}" class="list-group-item list-group-item-action">
    544. <div class="d-flex align-items-center">
    545. <div>
    546. <span class="avatar cover-image avatar-sm bg-white text-default avatar-rounded me-2">
    547. <img src="{{ forum.avatar }}" alt="img" class="avatar-img">
    548. </span>
    549. </div>
    550. <div class="ms-2 w-100">
    551. <div class="d-flex justify-content-between align-items-center">
    552. <div>
    553. <span class="fw-semibold me-1">{{ forum.username }}</span>
    554. <span class="text-muted fs-12">:</span>
    555. <span class="text-truncate" >{{ forum.title }}</span>
    556. </div>
    557. <div class="text-muted fs-11 ms-3">{{ forum.last_post_date|date('h:m:s d.m.Y') }}</div>
    558. </div>
    559. <div class="text-muted fs-12 mt-1">{{ truncateWord(forum.message, 220) }}</div>
    560. </div>
    561. </div>
    562. </a>
    563. {%endfor%}
    564. {%endif%}
    565. </div>
    566. {%else%}
    567. {{ forum().getMessageError() }}
    568. {%endif%}
    569. </div>
    570. </div>
    571. </div>
    572. </div>
    573. {%endif%}
    574. {{ include('other/startpack.html') }}
    575. </div>
    576. {%endblock%}
    577. {%block js%}
    578. <script>
    579. $(document).on('click', '.removeHWID', function () {
    580. var obj = $(this).data('obj');
    581. var account = $(this).data('account');
    582. AjaxSend('/player/reset/hwid', 'POST', {
    583. player_id: obj,
    584. account: account
    585. }).then(function (response) {
    586. console.log(response);
    587. });
    588. });
    589. $(document).on('click', '.btn-open-shop', function () {
    590. var startpackId = $(this).data('startpack-id');
    591. var itemList = $('#item_list_startpack_' + startpackId);
    592. var startpackName = itemList.closest('.p-4').find('.startpack_name').text();
    593. var startpackCost = itemList.closest('.p-4').find('.startpack_cost').text();
    594. var itemListHtml = '<h5>' + startpackName + '</h5>';
    595. itemList.find('li').each(function () {
    596. var itemSrc = $(this).data('item-src');
    597. var itemName = $(this).data('item-name');
    598. var itemEnchant = $(this).data('item-enchant');
    599. var itemCount = $(this).data('item-count');
    600. itemListHtml += '<div class="item">';
    601. itemListHtml += '<img src="' + itemSrc + '" alt="" class="avatar avatar-sm ">';
    602. itemListHtml += '<span class="text-muted">';
    603. if (itemEnchant > 0) {
    604. itemListHtml += '<span class="badge bg-danger text-white ms-1">+' + itemEnchant + '</span>';
    605. }
    606. itemListHtml += ' ' + itemName;
    607. itemListHtml += '</span>';
    608. itemListHtml += '</div>';
    609. });
    610. $('.StartPackCostBuy').text(startpackCost);
    611. $('#Startpackpurchase').attr('data-startpack-id', startpackId);
    612. $('#StartpackWarehouse').attr('data-startpack-id', startpackId);
    613. // Вставка списка предметов в модальное окно
    614. $('#itemListSellStartpack').html(itemListHtml);
    615. });
    616. $(document).on('click', '#Startpackpurchase', function () {
    617. const $startpackTab = $('#StartpackSendItemsToPlayer');
    618. const canSendItemsNow = $startpackTab.data('canSend') !== false;
    619. if (!canSendItemsNow) {
    620. const lockMessage = $startpackTab.data('lockMessage') || 'Отправка предметов временно недоступна. Попробуйте позже.';
    621. if (typeof noticeError === 'function') {
    622. noticeError(lockMessage);
    623. } else {
    624. alert(lockMessage);
    625. }
    626. return;
    627. }
    628. var startpackId = $(this).attr('data-startpack-id');
    629. var selectedOption = $('#StartpackPlayer option:selected');
    630. var account = selectedOption.attr('data-account');
    631. var player = selectedOption.val();
    632. AjaxSend('/startpack/purchase', 'POST', {
    633. account: account,
    634. player: player,
    635. startpackId: startpackId,
    636. }).then(function (response) {
    637. $('#openShopStartpack').modal('hide');
    638. $('.StartPackCostBuy').text(response.sphereCoin);
    639. });
    640. });
    641. $(document).on('click', '#StartpackWarehouse', function () {
    642. var startpackId = $(this).attr('data-startpack-id');
    643. var selectedOption = $('#StartpackPlayer option:selected');
    644. var account = selectedOption.attr('data-account');
    645. var player = selectedOption.val();
    646. AjaxSend('/startpack/purchase/warehouse', 'POST', {
    647. account: account,
    648. player: player,
    649. startpackId: startpackId,
    650. }).then(function (response) {
    651. $('#openShopStartpack').modal('hide');
    652. $('.StartPackCostBuy').text(response.sphereCoin);
    653. });
    654. });
    655. </script>
    656. <script>
    657. $(document).on('click', '.btn-change-password', function () {
    658. var account = $(this).data('account');
    659. $('.account_new_change_password').text(account);
    660. $('.account_new_change_password_input').val(account);
    661. });
    662. </script>
    663. <script>
    664. function transliterate(text) {
    665. const layoutMap = {
    666. 'й': 'q', 'ц': 'w', 'у': 'e', 'к': 'r', 'е': 't', 'н': 'y', 'г': 'u', 'ш': 'i', 'щ': 'o', 'з': 'p', 'х': '[', 'ъ': ']',
    667. 'ф': 'a', 'ы': 's', 'в': 'd', 'а': 'f', 'п': 'g', 'р': 'h', 'о': 'j', 'л': 'k', 'д': 'l', 'ж': ';', 'э': "'",
    668. 'я': 'z', 'ч': 'x', 'с': 'c', 'м': 'v', 'и': 'b', 'т': 'n', 'ь': 'm', 'б': ',', 'ю': '.',
    669. 'і': 's', 'ї': ']', 'є': "'",
    670. 'Й': 'Q', 'Ц': 'W', 'У': 'E', 'К': 'R', 'Е': 'T', 'Н': 'Y', 'Г': 'U', 'Ш': 'I', 'Щ': 'O', 'З': 'P', 'Х': '[', 'Ъ': ']',
    671. 'Ф': 'A', 'Ы': 'S', 'В': 'D', 'А': 'F', 'П': 'G', 'Р': 'H', 'О': 'J', 'Л': 'K', 'Д': 'L', 'Ж': ';', 'Э': "'",
    672. 'Я': 'Z', 'Ч': 'X', 'С': 'C', 'М': 'V', 'И': 'B', 'Т': 'N', 'Ь': 'M', 'Б': ',', 'Ю': '.',
    673. 'І': 'S', 'Ї': ']', 'Є': "'",
    674. 'α': 'a', 'β': 'b', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'h', 'θ': 'u', 'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm',
    675. 'ν': 'n', 'ξ': 'j', 'ο': 'o', 'π': 'p', 'ρ': 'r', 'σ': 's', 'ς': 's', 'τ': 't', 'υ': 'y', 'φ': 'f', 'χ': 'x', 'ψ': 'c', 'ω': 'v',
    676. 'Α': 'A', 'Β': 'B', 'Γ': 'G', 'Δ': 'D', 'Ε': 'E', 'Ζ': 'Z', 'Η': 'H', 'Θ': 'U', 'Ι': 'I', 'Κ': 'K', 'Λ': 'L', 'Μ': 'M',
    677. 'Ν': 'N', 'Ξ': 'J', 'Ο': 'O', 'Π': 'P', 'Ρ': 'R', 'Σ': 'S', 'Τ': 'T', 'Υ': 'Y', 'Φ': 'F', 'Χ': 'X', 'Ψ': 'C', 'Ω': 'V',
    678. 'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u', 'ý': 'y',
    679. 'à': 'a', 'è': 'e', 'ì': 'i', 'ò': 'o', 'ù': 'u',
    680. 'â': 'a', 'ê': 'e', 'î': 'i', 'ô': 'o', 'û': 'u',
    681. 'ã': 'a', 'õ': 'o', 'ñ': 'n', 'ç': 'c', 'ë': 'e', 'ï': 'i', 'ü': 'u',
    682. 'Á': 'A', 'É': 'E', 'Í': 'I', 'Ó': 'O', 'Ú': 'U', 'Ý': 'Y',
    683. 'À': 'A', 'È': 'E', 'Ì': 'I', 'Ò': 'O', 'Ù': 'U',
    684. 'Â': 'A', 'Ê': 'E', 'Î': 'I', 'Ô': 'O', 'Û': 'U',
    685. 'Ã': 'A', 'Õ': 'O', 'Ñ': 'N', 'Ç': 'C', 'Ë': 'E', 'Ï': 'I', 'Ü': 'U',
    686. 'ß': 'ss'
    687. };
    688. let result = '';
    689. for (let i = 0; i < text.length; i++) {
    690. const char = text[i];
    691. result += layoutMap[char] || char;
    692. }
    693. return result;
    694. }
    695. function updateCounterUI() {
    696. const $input = $('#accountRegistration');
    697. const $counter = $('#accountCharCounter');
    698. const $prefixButton = $('.account_prefix');
    699. if ($input.length === 0 || $counter.length === 0) {
    700. return;
    701. }
    702. const maxChars = parseInt($input.attr('maxlength'), 10);
    703. const prefixLen = $prefixButton.length > 0 ? $prefixButton.text().trim().length : 0;
    704. const currentLen = $input.val().length;
    705. const remaining = maxChars - prefixLen - currentLen;
    706. $counter.text('{{ phrase(312) }}: ' + remaining);
    707. if (remaining < 0) {
    708. $counter.removeClass('text-muted').addClass('text-danger');
    709. } else {
    710. $counter.removeClass('text-danger').addClass('text-muted');
    711. }
    712. }
    713. function enforceCharacterLimit() {
    714. const $input = $('#accountRegistration');
    715. if ($input.length === 0) return;
    716. const maxChars = parseInt($input.attr('maxlength'), 10);
    717. const prefixLen = $('.account_prefix').text().trim().length;
    718. const allowedInputLength = maxChars - prefixLen;
    719. if ($input.val().length > allowedInputLength) {
    720. const truncatedValue = $input.val().substring(0, allowedInputLength);
    721. $input.val(truncatedValue);
    722. }
    723. }
    724. $(document).on('input', '#accountRegistration', function () {
    725. const originalValue = $(this).val();
    726. let newValue = transliterate(originalValue);
    727. newValue = newValue.replace(/[^a-zA-Z0-9_]/g, '');
    728. if (originalValue !== newValue) {
    729. const start = this.selectionStart;
    730. const end = this.selectionEnd;
    731. $(this).val(newValue);
    732. this.setSelectionRange(start, end);
    733. }
    734. enforceCharacterLimit();
    735. updateCounterUI();
    736. });
    737. $(document).on('input', '#passwordRegistration', function () {
    738. const originalValue = $(this).val();
    739. let newValue = transliterate(originalValue);
    740. newValue = newValue.replace(/[^a-zA-Z0-9_]/g, '');
    741. if (originalValue !== newValue) {
    742. const start = this.selectionStart;
    743. const end = this.selectionEnd;
    744. $(this).val(newValue);
    745. this.setSelectionRange(start, end);
    746. }
    747. });
    748. updateCounterUI();
    749. $(document).on('click', '.account_prefix', function () {
    750. AjaxSend('/registration/account/prefix', 'POST', {}, true).then(function (prefix) {
    751. if (prefix.type === "notice") {
    752. responseAnalysis(prefix);
    753. return;
    754. }
    755. $('.account_prefix').text(prefix);
    756. updateCounterUI();
    757. });
    758. });
    759. $(document).on('click', '#togglePassword', function () {
    760. const passwordField = $('#passwordRegistration');
    761. const passwordFieldType = passwordField.attr('type');
    762. const toggleIcon = $(this).find('i');
    763. if (passwordFieldType === 'password') {
    764. passwordField.attr('type', 'text');
    765. toggleIcon.removeClass('bi-eye-fill').addClass('bi-eye-slash-fill');
    766. } else {
    767. passwordField.attr('type', 'password');
    768. toggleIcon.removeClass('bi-eye-slash-fill').addClass('bi-eye-fill');
    769. }
    770. });
    771. $(document).on('change', '#hidePasswordRegistration', function () {
    772. const passwordField = $('#passwordRegistration');
    773. const toggleIcon = $('#togglePassword').find('i');
    774. if ($(this).is(':checked')) {
    775. passwordField.attr('type', 'password');
    776. toggleIcon.removeClass('bi-eye-slash-fill').addClass('bi-eye-fill');
    777. } else {
    778. passwordField.attr('type', 'text');
    779. toggleIcon.removeClass('bi-eye-fill').addClass('bi-eye-slash-fill');
    780. }
    781. });
    782. </script>
    783. <script>
    784. function messageRegistrationAccount(response) {
    785. if (response.ok) {
    786. $("#registrationDataMessage").removeClass("d-none");
    787. $("#registrationMessage").removeClass("bg-success-transparent");
    788. $("#registrationMessage").removeClass("bg-danger-transparent");
    789. $("#registrationMessage").addClass("bg-success-transparent");
    790. $("#registrationMessage").text(response.message);
    791. } else {
    792. $("#registrationDataMessage").removeClass("d-none");
    793. $("#registrationMessage").removeClass("bg-danger-transparent");
    794. $("#registrationMessage").removeClass("bg-success-transparent");
    795. $("#registrationMessage").addClass("bg-danger-transparent");
    796. $("#registrationMessage").text(response.message);
    797. }
    798. }
    799. $(document).ready(function () {
    800. var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
    801. var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
    802. return new bootstrap.Tooltip(tooltipTriggerEl);
    803. });
    804. $(document).on('click', '.toggle-characters', function () {
    805. var accountName = $(this).data('account');
    806. var characterRow = $('.character-row-' + CSS.escape(accountName));
    807. $('.character-container').not(characterRow).hide();
    808. characterRow.toggle();
    809. });
    810. });
    811. </script>
    812. {%endblock%}
    /home/lineagx/www/vendor/twig/twig/src/Environment.php (строка 382)
    Twig\Environment->compileSource(Object(Twig\Source))
    /home/lineagx/www/vendor/twig/twig/src/Template.php (строка 282)
    Twig\Environment->loadTemplate('__TwigTemplate_c1c11544846bd9b...', 'brief_statistics/tpl/brief_sta...', NULL)
    /home/lineagx/www/vendor/twig/twig/src/Environment.php(392) : eval()'d code (строка 1066)
    Twig\Template->loadTemplate('brief_statistics/tpl/brief_sta...', 'index.html', 569)
    /home/lineagx/www/vendor/twig/twig/src/Template.php (строка 430)
    __TwigTemplate_b3d3adc28658407de0038475a51107e3->block_content(Array(19), Array(5))
    /home/lineagx/www/vendor/twig/twig/src/Environment.php(392) : eval()'d code (строка 269)
    Twig\Template->yieldBlock('content', Array(14), Array(5))
    /home/lineagx/www/vendor/twig/twig/src/Template.php (строка 360)
    __TwigTemplate_64a03cda582864f9912d97731d1f6eb7->doDisplay(Array(14), Array(5))
    /home/lineagx/www/vendor/twig/twig/src/Environment.php(392) : eval()'d code (строка 45)
    Twig\Template->yield(Array(13), Array(5))
    /home/lineagx/www/vendor/twig/twig/src/Template.php (строка 360)
    __TwigTemplate_b3d3adc28658407de0038475a51107e3->doDisplay(Array(13), Array(3))
    /home/lineagx/www/vendor/twig/twig/src/Template.php (строка 327)
    Twig\Template->yield(Array(13), Array(3))
    /home/lineagx/www/vendor/twig/twig/src/TemplateWrapper.php (строка 45)
    Twig\Template->display(Array(13), Array(0))
    /home/lineagx/www/src/template/tpl.php (строка 2824)
    Twig\TemplateWrapper->display(Array(13))
    /home/lineagx/www/src/controller/main/main.php (строка 18)
    Ofey\Logan22\template\tpl::display('index.html')
    [Внутренняя функция]
    Ofey\Logan22\controller\main\main::index()
    /home/lineagx/www/vendor/bramus/router/src/Bramus/Router/Router.php (строка 430)
    call_user_func_array('Ofey\Logan22\controller\main\m...', Array(0))
    /home/lineagx/www/vendor/bramus/router/src/Bramus/Router/Router.php (строка 416)
    Bramus\Router\Router->invoke('Ofey\Logan22\controller\main\m...', Array(0))
    /home/lineagx/www/vendor/bramus/router/src/Bramus/Router/Router.php (строка 280)
    Bramus\Router\Router->handle(Array(164), true)
    /home/lineagx/www/src/route/route_registry.php (строка 124)
    Bramus\Router\Router->run()
    /home/lineagx/www/index.php (строка 11)
    require('/home/lineagx/www/src/route/ro...')

    Переменные доступные в шаблоне:

    __route__ (string)
    "/main"
    dir (string)
    ""
    protocol (string)
    "https"
    path (string)
    ""
    template (string)
    "/src/template/sphere/"
    pointTime (string)
    "0.17"
    page_external_css (array)
    []
    page_external_js (array)
    []
    page_inline_css (string)
    ""
    page_inline_js (string)
    "$(document).on('click', '.removeHWID', function () { var obj = $(this).data('obj'); var acco..."
    page_title (string)
    "Личный кабинет"
    time (string)
    "2025-12-22 19:48:26"
    licenseCheck (NULL)
    null

    Возможные решения

    Проверьте синтаксис и логику шаблона.
    Убедитесь, что все переменные и функции определены и доступны.
    Проверьте, не используются ли устаревшие методы или функции.