
Персоналізація навчання в Articulate Rise 360: практичний гайд від Селезень Олега
Співробітник відкриває курс і бачить звернення на своє ім’я, текст з урахуванням його гендеру, а іноді навіть свій аватар чи фото. Звучить круто, чи не так? Для слухача це начебто дрібна деталь, а для L&D фахівців – це інструмент, який суттєво підвищує залученість та ефективність навчання. Персоналізовані курси краще утримують увагу й створюють відчуття цінності: «цей курс зроблено саме для мене».
До того ж усі дані вже зберігаються в LMS. Тож достатньо лише налаштувати так, щоб вони автоматично підвантажувались у курс.
Разом з Олегом Селезнем ми вже ділилися досвідом використання користувацьких даних у Articulate Storyline. Тепер розглянемо інший інструмент – Articulate Rise 360, який часто обирають за простоту, швидкість і сучасний дизайн.
У цій статті ми зосередимось на тому, як поєднати зручність Rise із можливостями персоналізації через LMS Collaborator. Своїм досвідом поділиться Олег Селезень – керівник відділу дистанційного навчання компанії VIYAR.
Переходимо від теорії до практики: які дані з LMS Collaborator можна використати для персоналізації курсів у Rise 360?
Олег Селезень – керівник відділу дистанційного навчання компанії VIYAR
Які параметри ми можемо отримати?
Усі дані передаються з LMS Collaborator у SCORM через змінну cmi.core.student_info. Їх також можна використати для персоналізації в Articulate Rise 360.
На відміну від Storyline, у Rise 360 немає прямої роботи з тригерами чи умовами. Проте отримані значення можна застосовувати для персоналізації: налаштовувати сценарії навчання, відображати потрібні блоки та підлаштовувати звернення.
Щоб реалізувати це, знадобиться невелика взаємодія з кодом. Але не хвилюйтеся – все максимально просто.😊
Далі покроково, як саме це виглядає на практиці.
Крок 1. Форматування змінних у курсі Articulate Rise 360
Щоб підтягнути дані користувача, достатньо вказати змінну з LMS Collaborator, обрамлену знаками відсотка, наприклад:
- user_id → %user_id%
- user_firstname → %user_firstname%
- user_email → %user_email%
Коли користувач відкриває курс, система автоматично замінює значення на його справжні дані.
Важливо: самі по собі вони не працюють. Якщо не додати код-зв’язку з LMS і не налаштувати публікацію під SCORM, у курсі залишиться просто %user_firstname%.
Що робити далі?
-
- Опублікуйте курс у Rise 360: Publish → LMS, обираємо SCORM 1.2 або 2004 і зберігаємо ZIP.
- Розпакуйте архів у будь-яку папку.
- Знайдіть та відкрийте папку scormcontent.
- Знайдіть файл index.html і відкрийте його у будь-якому редакторі.
- Додайте ось цей код одразу після тега <title>:
Код
<script defer src="https://cdn.jsdelivr.net/gh/asimut/user-variables/selezen_v1.js"></script>
Тепер курс підтягуватиме дані користувача.
Крок 2. Фото користувача замість стандартних зображень
Ще один простий спосіб персоналізації – заміна картинок на фото конкретного користувача. Це працює в будь-якому місці курсу, де є зображення.
Це дає відчутну персоналізацію «в обличчя» без зміни верстки блоку та працює в будь-яких місцях курсу, де є зображення.
Як це зробити:
- Відкрийте потрібний блок у Articulate Rise 360 та натисните Image Alt Text.
- Уведіть replace_user_img та підтвердіть.
- За потреби повторіть для інших зображень.
При відкритті курсу система автоматично підставить фото користувача з LMS.
Крок 3. Звернення за гендером
Коли LMS передає інформацію про стать користувача (наприклад, male або female), у курсі ми можемо автоматично змінювати слова та їх закінчення відповідно до потрібного гендера.
Приклад такого текстового блоку в Articulate Rise 360:
На зображені стандартний текстовий блок. Саме у таких блоках можна налаштувати заміну, щоб звернення виглядали природно: «ти завершив» або «ти завершила».
Для цього потрібно підготувати невеликий скрипт, який перевіряє отримане значення і підставляє потрібний варіант.
На що варто звернути увагу:
За замовчуванням усі звернення можна залишати у чоловічому роді. Потім система буде змінювати їх на потрібний варіант у тих блоках, де ви це вкажете. Важливо не підключати заміну скрізь, інакше можуть випадково змінитися слова поза контекстом звернень. Це виглядатиме як помилка.
Як це зробити:
1. Додаємо код, що отримує дані з LMS.
2. Додаємо модуль для коректних звернень за гендером:
– підключаєте основний скрипт із логікою заміни (він виконує пошук і підміну);
– у конфігурації задаємо слова, які будуть замінюватися (наприклад, завершив → завершила) та ID блоків (targetBlocks), де ці заміни дозволені.
У результаті система автоматично підставлятиме правильні варіанти звернень залежно від гендеру користувача.
Код
<script defer src="https://cdn.jsdelivr.net/gh/asimut/user_gender_replacer/selezen_v1.js"></script>
<script>
// Конфіг (тільки дані, ніякої логіки)
window.GenderReplacer = {
debug: false,
genderReplacements: {
female: {
'завершив': 'завершила',
'дізнався': 'дізналася',
'ознайомився': 'ознайомилася',
'зрозумів': 'зрозуміла',
'пройшов': 'пройшла',
'успішно завершив': 'успішно завершила',
'готовий': 'готова',
'вивчив': 'вивчила',
'переглянув': 'переглянула',
'опанував': 'опанувала',
'засвоїв': 'засвоїла',
'досяг': 'досягла',
'здобув': 'здобула',
'отримав': 'отримала',
'виконав': 'виконала',
'склав': 'склала',
'закінчив': 'закінчила'
}
},
targetBlocks: [
'cmckqk51l001c357b79yaongk'
]
};
</script>
Важливо: нижній блок – це лише конфігурація (дані). Саму підміну виконує основний скрипт із логікою заміни, підключений рядком вище.
Робота з ідентифікаторами блоків
Щоб система знала, у якому саме текстовому блоці потрібно робити заміни, ви вказуєте його у списку targetBlocks. Для цього кожен блок у Rise має свій унікальний ID.
Є два способи знайти ID:
Спосіб 1.
Через інструменти розробника DevTools. Якщо ви впевнено користуєтеся консоллю браузера, відкрийте код сторінки й скопіюйте потрібний ідентифікатор.
Спосіб 2.
За допомогою плагіна. Зручніше і швидше, особливо якщо не хочете працювати з кодом напряму.
Також ви можете розширювати список слів для заміни. Якщо серед наведених прикладів немає потрібних, сміливо додавайте власні варіанти у конфігурацію.
Щоб спростити пошук ідентифікаторів блоків, зручно користуватися плагіном.
Як встановити плагін:
- Відкрийте браузер і перейдіть у розділ Розширення – Керувати розширенями.
- Увімкніть Режим розробника.
- Завантажте архів із плагіном, розпакуйте його й додайте розширення з цієї папки у браузер;
Посилання на архів - Після цього він автоматично з’явиться у вашій сторінці редагування курсу.
Плагін можна вмикати або вимикати будь-коли. З його допомогою ви швидко знаходите ID блоків і додаєте їх у конфігурацію.
Коли ми навчилися швидко знаходити ID за допомогою плагіна, можемо використати ці ідентифікатори не лише для точкових замін, а й для керування видимістю цілих фрагментів курсу. Це дозволить показувати різним департаментам свій контент у межах одного курсу – без дублювання матеріалів і зайвих копій.
Крок 4. Керування контентом для різних департаментів
Ми вже маємо всі змінні з LMS, зокрема user_department. Це означає, що департамент не потрібно шукати: система зчитує його з даних користувача і на цій основі відкриває потрібні блоки курсу. Для цього потрібно лише вказати назву департаменту, який є в LMS та перелік блоків, які він має бачити. Для всіх інших працює гілка default.
Код
<script>
(function () {
'use strict';
// Конфіг
// Заповни mapping під свої департаменти та їхні data-block-id
window.DepartmentBlocks = window.DepartmentBlocks || {
debug: false,
mapping: {
'назава департаменту': ['ТУТ ВКАЖИТИ ІD БЛОКУ'], // назва має буди із меленької літери
'default': ['ТУТ ВКАЖИТИ ІD БЛОКУ'], // блок для всіх інших
},
};
const DB = window.DepartmentBlocks;
const ATTR_CANDIDATES = ['data-block-id','data-blockid','data-block','blockid','data-id','id'];
const HIDE_CLASS = 'dept-hidden-by-mapping';
const STYLE_ID = 'dept-visibility-style';
const OBS_CONFIG = { childList: true, subtree: true, attributes: false, characterData: false };
let allListedIds = new Set(); // усі ID, що зустрічаються у mapping (для первинного приховування)
let allowedIds = new Set(); // ID, дозволені для поточного департаменту
let currentDept = null; // нормалізоване значення поточного департаменту
let initialized = false; // щоб не ініціалізувати двічі
let mainObserver = null;
const log = (...args) => { if (DB.debug) console.log('[DepartmentBlocks]', ...args); };
const warn = (...args) => console.warn('[DepartmentBlocks]', ...args);
function normalize(str) {
return (str == null) ? '' : String(str).toLowerCase().trim();
}
function injectStyleOnce(doc = document) {
if (doc.getElementById(STYLE_ID)) return;
const style = doc.createElement('style');
style.id = STYLE_ID;
style.textContent = `
.${HIDE_CLASS} { display: none !important; }
`;
doc.head.appendChild(style);
}
function getBlockIdFromElement(el) {
for (const attr of ATTR_CANDIDATES) {
if (el.hasAttribute && el.hasAttribute(attr)) {
const v = el.getAttribute(attr);
if (v && v.trim()) return v.trim();
}
}
return null;
}
function collectAllListedIds() {
allListedIds = new Set();
const map = DB.mapping || {};
Object.values(map).forEach(arr => {
(arr || []).forEach(id => allListedIds.add(String(id)));
});
log('All listed IDs:', Array.from(allListedIds));
}
function computeAllowedIdsFor(dept) {
const mappingKey = DB.mapping?.[dept] ? dept : 'default';
allowedIds = new Set(DB.mapping?.[mappingKey] || []);
log('Allowed IDs for', dept, '(using mapping key:', mappingKey, '):', Array.from(allowedIds));
}
function isListed(id) {
return allListedIds.has(id);
}
function shouldShow(id) {
return allowedIds.has(id);
}
function hideEl(el) {
if (!el.classList.contains(HIDE_CLASS)) {
el.classList.add(HIDE_CLASS);
}
}
function showEl(el) {
if (el.classList.contains(HIDE_CLASS)) {
el.classList.remove(HIDE_CLASS);
}
}
function initialHideListedBlocks(root = document) {
const candidates = root.querySelectorAll('*');
candidates.forEach(el => {
const id = getBlockIdFromElement(el);
if (id && isListed(id)) hideEl(el);
});
}
function applyDepartmentVisibility(root = document) {
const candidates = root.querySelectorAll('*');
candidates.forEach(el => {
const id = getBlockIdFromElement(el);
if (!id) return;
if (!isListed(id)) return;
if (shouldShow(id)) showEl(el);
else hideEl(el);
});
}
function isUserDataReady() {
const sources = [
window.UserVariables?.data,
window.UserVariables2?.data,
window.userData
];
return sources.some(d => d && typeof d === 'object');
}
function getUserDepartment() {
if (DB.overrideDepartment) return normalize(DB.overrideDepartment);
const sources = [
window.UserVariables?.data,
window.UserVariables2?.data,
window.userData
];
for (const d of sources) {
if (!d || typeof d !== 'object') continue;
for (const key of ['user_department','department','dept','user_dept','org_unit','division']) {
if (d[key]) return normalize(d[key]);
}
}
return '';
}
function processIframe(iframe) {
try {
const doc = iframe?.contentDocument;
if (!doc) return;
injectStyleOnce(doc);
initialHideListedBlocks(doc);
if (currentDept) applyDepartmentVisibility(doc);
const obs = new MutationObserver(() => {
if (!currentDept) initialHideListedBlocks(doc);
else applyDepartmentVisibility(doc);
});
obs.observe(doc.documentElement, OBS_CONFIG);
} catch (e) {}
}
function watchIframes() {
const iframes = document.querySelectorAll('iframe');
iframes.forEach(ifr => {
if (ifr._deptBound) return;
ifr._deptBound = true;
if (ifr.contentDocument && ifr.contentDocument.readyState !== 'loading') {
processIframe(ifr);
} else {
ifr.addEventListener('load', () => processIframe(ifr), { passive: true });
}
});
}
function applyNow(root = document) {
injectStyleOnce(document);
watchIframes();
if (!currentDept) initialHideListedBlocks(root);
else applyDepartmentVisibility(root);
}
function startObserver() {
if (mainObserver) return;
mainObserver = new MutationObserver(() => applyNow(document));
mainObserver.observe(document.documentElement, OBS_CONFIG);
['#app','.rise-player','[data-rise]','.course-container'].forEach(sel => {
const c = document.querySelector(sel);
if (c && !c._deptObserver) {
const o = new MutationObserver(() => applyNow(c));
o.observe(c, OBS_CONFIG);
c._deptObserver = o;
}
});
}
function resolveDepartmentAndApply() {
const dept = getUserDepartment();
if (!dept) {
log('user_department не знайдено (поки що). Працюємо у режимі initial-hide.');
return;
}
if (dept === currentDept) return;
currentDept = dept;
computeAllowedIdsFor(currentDept);
applyNow(document);
}
function waitForDataThenInit() {
if (initialized) return;
initialized = true;
injectStyleOnce(document);
collectAllListedIds();
initialHideListedBlocks(document);
watchIframes();
startObserver();
let tries = 0;
const quick = setInterval(() => {
tries++;
if (isUserDataReady()) {
resolveDepartmentAndApply();
if (currentDept) clearInterval(quick);
}
if (tries >= 20) clearInterval(quick);
}, 500);
setInterval(resolveDepartmentAndApply, 2000);
}
DB.forceApply = () => applyNow(document);
DB.setDepartment = (deptName) => {
DB.overrideDepartment = deptName;
resolveDepartmentAndApply();
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(waitForDataThenInit, 50), { once: true });
} else {
setTimeout(waitForDataThenInit, 50);
}
})();
</script>
Як це працює:
- на старті всі «керовані» тимчасово блоки приховані;
- коли система зчитує user_department, вона показує лише ті блоки, які ви закріпили за цим департаментом;
- блоки з інших списків залишаються прихованими, а ті, яких немає в mapping – видимі;
- якщо департамент не знайдено або він порожній – користувач бачить default.
В результаті кожен співробітник отримує тільки свій контент – без дублювання курсу та зайвих версій.
Підсумуємо
Тепер у вас є три готові інструменти:
- Скрипт №1 – отримує дані користувача, підтягуючи назву підрозділу або персональне фото.
- Скрипт №2 – змінює слова й закінчення в тексті залежно від гендеру користувача.
- Скрипт №3 – керує контентом: приховує чи відображає блоки курсу для різних департаментів.
Вам не обов’язково підключати всі одразу – використовуйте тільки ті, що справді потрібні для вашого курсу.
Останній крок: пакування та завантаження курсу в LMS
Коли всі зміни внесені, залишилося зробити найпростіше – правильно запакувати курс.
Як це зробити:
- Виділіть усі файли та папки всередині проєкту (але не саму верхню папку).
- Запакуйте їх у форматі ZIP за допомогою будь-якого зручного архіватора.
- Завантажте отриманий ZIP файл в LMS Collaborator.
Висновок
В статті ми пройшли весь шлях: від того, як підтягувати дані з LMS і підставляти реальні фото, до зміни звернень за гендером і керування блоками курсу для різних департаментів.
Тепер курс працює «з коробки» та перетворюється на багато версій без дублювання та зайвої роботи.
Що ви отримуєте:
- Персоналізація – звернення по імені, фото користувача, тексти під стать.
- Контроль над контентом – показуєте різні блоки для різних департаментів.
- Економія часу – менше ручної роботи, менше дублювання.
- Зручність для команди – навчання стає зрозумілим, живим і близьким до потреб співробітників.
Головна ідея проста: менше технічних маніпуляцій, більше персоналізації, гнучкості та цінності для всіх.😊
