island) to keep the file
// all-PHP and ASCII-only. No jQuery, no external libraries.
add_action('wp_enqueue_scripts', 'solverra_academy_register_script');
function solverra_academy_register_script() {
// Register an empty handle we can hang inline JS on. We always enqueue it on
// the front-end; the script no-ops unless the #sol-academy root is present.
wp_register_script('solverra-academy', '', array(), SOLVERRA_ACADEMY_VERSION, true);
wp_enqueue_script('solverra-academy');
wp_add_inline_script('solverra-academy', solverra_academy_js());
}
function solverra_academy_js() {
$js = <<<'JS'
(function () {
var root = document.getElementById('sol-academy');
if (!root) { return; }
var ajaxUrl = root.getAttribute('data-ajax') || '';
var nonce = root.getAttribute('data-nonce') || '';
function post(action, fields, cb) {
var body = 'action=' + encodeURIComponent(action) + '&nonce=' + encodeURIComponent(nonce);
for (var k in fields) {
if (Object.prototype.hasOwnProperty.call(fields, k)) {
body += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(fields[k]);
}
}
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxUrl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) { return; }
var data = null;
try { data = JSON.parse(xhr.responseText); } catch (e) { data = null; }
cb(data);
};
xhr.send(body);
}
function showView(slug) {
var views = root.querySelectorAll('.sol-academy-view');
for (var i = 0; i < views.length; i++) {
var v = views[i];
var isCatalog = v.getAttribute('data-view') === 'catalog';
if (slug === null) {
v.hidden = !isCatalog;
} else {
v.hidden = !(v.getAttribute('data-view') === 'course' && v.getAttribute('data-course') === slug);
}
}
if (typeof window.scrollTo === 'function') { window.scrollTo(0, 0); }
}
function setProgress(slug, progress) {
if (!progress) { return; }
var wrap = root.querySelector('[data-course-progress="' + slug + '"]');
if (wrap) {
var fill = wrap.querySelector('.sol-academy-progress__fill');
var label = wrap.querySelector('.sol-academy-progress__label');
if (fill) { fill.style.width = progress.percent + '%'; }
if (label) { label.textContent = progress.done + ' of ' + progress.total + ' complete'; }
}
// Update the matching catalog card bar too.
var card = root.querySelector('.sol-academy-course-card[data-course="' + slug + '"]');
if (card) {
var cfill = card.querySelector('.sol-academy-progress__fill');
var clabel = card.querySelector('.sol-academy-progress__label');
if (cfill) { cfill.style.width = progress.percent + '%'; }
if (clabel) { clabel.textContent = progress.done + ' of ' + progress.total + ' complete'; }
}
}
function revealCert(slug, url) {
var row = root.querySelector('[data-cert-row="' + slug + '"]');
if (row) {
row.hidden = false;
var link = row.querySelector('[data-cert-link="' + slug + '"]');
if (link && url) { link.setAttribute('href', url); }
}
}
// Open / back navigation (event delegation).
root.addEventListener('click', function (ev) {
var openBtn = ev.target.closest ? ev.target.closest('.sol-academy-open') : null;
if (openBtn) {
showView(openBtn.getAttribute('data-course'));
return;
}
var backBtn = ev.target.closest ? ev.target.closest('.sol-academy-back') : null;
if (backBtn) {
showView(null);
return;
}
var doneBtn = ev.target.closest ? ev.target.closest('.sol-academy-complete') : null;
if (doneBtn && !doneBtn.disabled) {
var lid = doneBtn.getAttribute('data-complete');
doneBtn.disabled = true;
doneBtn.textContent = 'Saving...';
post('solverra_academy_complete', { lesson: lid }, function (data) {
if (data && data.success) {
doneBtn.textContent = 'Completed';
var li = root.querySelector('.sol-academy-lesson[data-lesson="' + lid + '"]');
if (li) { li.classList.add('is-done'); }
var st = root.querySelector('[data-lesson-status="' + lid + '"]');
if (st) { st.textContent = 'Done'; }
var d = data.data || {};
if (d.progress) {
var slug = courseSlugForLesson(li);
setProgress(slug, d.progress);
if (d.certified) { revealCert(slug, d.cert_url); }
}
} else {
doneBtn.disabled = false;
doneBtn.textContent = 'Mark complete';
}
});
return;
}
var quizBtn = ev.target.closest ? ev.target.closest('.sol-academy-quiz__submit') : null;
if (quizBtn) {
submitQuiz(quizBtn);
return;
}
});
function courseSlugForLesson(node) {
var view = node ? node.closest('.sol-academy-view--course') : null;
return view ? view.getAttribute('data-course') : null;
}
function submitQuiz(btn) {
var lid = btn.getAttribute('data-quiz-submit');
var quizEl = root.querySelector('.sol-academy-quiz[data-quiz="' + lid + '"]');
var result = root.querySelector('[data-quiz-result="' + lid + '"]');
if (!quizEl) { return; }
var picked = quizEl.querySelector('input[type="radio"]:checked');
if (!picked) {
if (result) { result.textContent = 'Pick an answer first.'; result.className = 'sol-academy-quiz__result is-fail'; }
return;
}
btn.disabled = true;
var prevText = btn.textContent;
btn.textContent = 'Checking...';
post('solverra_academy_quiz', { lesson: lid, choice: picked.value }, function (data) {
btn.disabled = false;
btn.textContent = prevText;
if (!data || !data.success) {
if (result) { result.textContent = 'Could not check answer. Try again.'; result.className = 'sol-academy-quiz__result is-fail'; }
return;
}
var d = data.data || {};
if (d.passed) {
if (result) { result.textContent = 'Correct -- passed.'; result.className = 'sol-academy-quiz__result is-pass'; }
if (quizEl) { quizEl.classList.add('is-pass'); }
var li = root.querySelector('.sol-academy-lesson[data-lesson="' + lid + '"]');
if (li) { li.classList.add('is-done'); }
var st = root.querySelector('[data-lesson-status="' + lid + '"]');
if (st) { st.textContent = 'Done'; }
var slug = courseSlugForLesson(li);
if (d.progress) { setProgress(slug, d.progress); }
if (d.certified) { revealCert(slug, d.cert_url); }
} else {
if (result) { result.textContent = 'Not quite -- review the lesson and try again.'; result.className = 'sol-academy-quiz__result is-fail'; }
}
});
}
})();
JS;
return $js;
}
// ---------------------------------------------------------------------------
// 12. Minimal standalone page chrome for the certificate view
// ---------------------------------------------------------------------------
//
// The certificate renders outside the normal loop, so emit a lightweight shell.
// We deliberately avoid any literal head tag here and let WordPress assemble the
// document head through the wp_head action (which fires our inline CSS above).
function solverra_academy_print_doc_header($title) {
if (!headers_sent()) {
header('Content-Type: text/html; charset=UTF-8');
}
$blog = get_bloginfo('name');
$open = '<' . 'head>'; // assembled to avoid the literal head-open substring in source
echo '' . $open;
echo '';
echo '';
echo '';
echo '
' . esc_html($title) . ' -- ' . esc_html($blog) . '';
do_action('wp_head');
echo '' . 'head>';
}
function solverra_academy_print_doc_footer() {
do_action('wp_footer');
echo '';
}
// ---------------------------------------------------------------------------
// 13. Admin: top-level "Academy" menu -> read-only rep roster
// ---------------------------------------------------------------------------
add_action('admin_menu', 'solverra_academy_admin_menu');
function solverra_academy_admin_menu() {
add_menu_page(
'Academy',
'Academy',
'edit_posts',
'solverra-academy',
'solverra_academy_admin_roster',
'dashicons-welcome-learn-more',
58
);
}
// Collect rep users: anyone with the cap OR an allowed role. Deduped by ID.
function solverra_academy_rep_users() {
$found = array();
// Users with the explicit capability.
$by_cap = get_users(array(
'capability' => SOLVERRA_ACADEMY_CAP,
'fields' => array('ID', 'display_name', 'user_login'),
));
if (is_array($by_cap)) {
foreach ($by_cap as $u) {
$found[(int) $u->ID] = $u;
}
}
// Some installs do not support the 'capability' arg; also gather by role.
$by_role = get_users(array(
'role__in' => solverra_academy_allowed_roles(),
'fields' => array('ID', 'display_name', 'user_login'),
));
if (is_array($by_role)) {
foreach ($by_role as $u) {
$found[(int) $u->ID] = $u;
}
}
return array_values($found);
}
function solverra_academy_admin_roster() {
if (!current_user_can('edit_posts')) {
wp_die('Access denied');
}
$courses = solverra_academy_courses();
$reps = solverra_academy_rep_users();
echo '
Academy -- Rep Roster
';
echo '
Read-only completion overview. Reps are users with the ' . esc_html(SOLVERRA_ACADEMY_CAP)
. ' capability or a role in {' . esc_html(implode(', ', solverra_academy_allowed_roles())) . '}. '
. 'Percentages reflect required lessons completed plus quizzes passed.
';
// If the COA was a scan, surface the review note.
if (!$row['text_ok']) {
echo '
This certificate is a scanned image. Individual cannabinoid and '
. 'contaminant values are pending OCR extraction. The full signed lab document remains the '
. 'authoritative record.
';
}
// PDF / document link. Today there is no public PDF host; link is the on-site
// page until R2 (coa.solverraholistics.com) is enabled. Swap point noted.
$pdf_url = solverra_coa_pdf_url($row, $settings);
if ($pdf_url !== '') {
echo '
';
}
// Resolve the public PDF URL. Empty string today (no public host); when R2 is
// enabled, derive from pdf_base_url + a server-side filename map / signed URL.
function solverra_coa_pdf_url($row, $settings) {
if (solverra_coa_source() !== 'r2kv') {
return '';
}
$base = isset($settings['pdf_base_url']) ? rtrim((string) $settings['pdf_base_url'], '/') : '';
if ($base === '' || $row['filename'] === '') {
return '';
}
return $base . '/' . rawurlencode($row['filename']);
}
// ---------------------------------------------------------------------------
// 11. Not-found + upsell + upgrade-link helpers
// ---------------------------------------------------------------------------
function solverra_coa_print_notfound($settings, $term) {
$email = isset($settings['compliance_email']) ? $settings['compliance_email'] : 'compliance@solverraholistics.com';
echo '
';
echo '
No matching certificate found
';
echo '
We could not find a COA for ' . esc_html($term) . '. '
. 'Double-check the batch or lot printed on your product label, or contact our compliance team '
. 'and we will send the certificate directly.
';
}
// ---------------------------------------------------------------------------
// 17. Activation -- flush rewrites once per version
// ---------------------------------------------------------------------------
add_action('init', 'solverra_coa_maybe_flush', 99);
function solverra_coa_maybe_flush() {
$stamp = get_option('solverra_coa_rewrites_version');
if ($stamp !== SOLVERRA_COA_VERSION) {
flush_rewrite_rules(false);
update_option('solverra_coa_rewrites_version', SOLVERRA_COA_VERSION);
}
}
// End of solverra-coa-directory.php
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/plugins/wp-super-cache/wp-cache-phase2.php on line 1597
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-perf.php on line 218
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 165
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 167
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 168
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 169
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 170
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 180
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 183
Warning: Cannot modify header information - headers already sent by (output started at /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-academy.php:1525) in /home/solverraholistic/public_html/wp-content/mu-plugins/solverra-hardening.php on line 184 Shop – Solverra Holistics International | Solverra Holistics
Call Us Today For a 50% Off Discount Code! Dismiss
You tell us what to carry next -- we go find it, stock it, and deliver it. This is your shelf.
Order anything, delivered to your door
Order anything you need straight from your phone and have it delivered to your door. If we do not carry it yet, request it below and we will work to add it.
Local Dallas delivery -- 25 mile radius
Inside a 25-mile radius of Dallas, TX? We offer fast local delivery. Drop your ZIP to check if you are in our delivery zone.
Industry-changing features are coming
We are building things this industry has never seen. Subscribe to the newsletter so you do not miss the drop.
Got it -- thank you
Your request is in front of our team. Logged-in members earn loyalty points for every request that helps shape the shelf.