Masukkan kata sandi untuk mengakses pengaturan ujian
Daftar Ujian & Mata Pelajaran
Kelola mata pelajaran, durasi, token masuk siswa, dan tautan Google Form
Mata Pelajaran & Kelas
Durasi
Token Masuk
Sumber Soal
Status
Aksi
Profil Sekolah & Pengaturan Keamanan
Sesuaikan kop sekolah, judul ujian, dan aturan anti-kecurangan
Tersimpan Otomatis
Integrasi Google Form ke Aplikasi Ujian (CBT)
Langkah mudah menghubungkan Google Form agar tampil dengan antarmuka CBT sekolah yang mewah
1
Buat Soal di Google Form Seperti Biasa
Buka Google Drive -> Buat Google Form baru. Tuliskan soal pilihan ganda (A, B, C, D, E) dan kunci jawaban sesuai kebutuhan Anda.
2
Buka Menu "Editor Skrip" (Apps Script)
Di kanan atas Google Form Anda, klik ikon titik tiga (⋮), lalu pilih menu Editor Skrip (<> Script editor).
3
Tempelkan (Paste) Kode Skrip & Simpan
Hapus semua kode bawaan di editor, lalu salin dan tempelkan seluruh kode di bawah ini. Klik ikon Disket (Simpan).
4
Terapkan Sebagai Aplikasi Web (Deploy as Web App)
Klik tombol biru Terapkan (Deploy) di kanan atas -> Pilih Penerapan baru (New deployment) -> Pilih tipe Aplikasi Web (Web app). PENTING: Pada kolom "Yang memiliki akses (Who has access)", pilih Siapa saja (Anyone) agar siswa dapat mengambil soal. Klik Terapkan dan berikan izin akses.
5
Salin URL Aplikasi Web & Masukkan ke Aplikasi Ujian
Salin URL yang berakhiran /exec, lalu tempelkan ke form input saat menambah/mengedit jadwal ujian di tab Daftar Ujian. Selesai!
Tips Pembuatan Soal Bergambar di Google Form
Skrip ini sudah mendukung 3 cara memasukkan gambar soal:
• Cara 1 (Paling Mudah): Klik tombol "Tambahkan Gambar" (ikon gambar di bilah samping kanan Google Form) tepat sebelum soal, atau klik ikon gambar di samping kotak teks pertanyaan.
• Cara 2 (Gambar pada Opsi Pilihan): Setiap opsi A, B, C, D, E juga bisa memiliki gambar sendiri.
• Cara 3: Menempelkan link share Google Drive atau link gambar langsung di kolom deskripsi soal.
Kode Skrip Google Form (Code.gs - Versi Bergambar):
function doGet(e) {
try {
var form = FormApp.getActiveForm();
if (!form) return ContentService.createTextOutput(JSON.stringify({ status: 'error', message: 'Form aktif tidak ditemukan' })).setMimeType(ContentService.MimeType.JSON);
var items = form.getItems();
var imageMapById = {};
var imageMapByTitle = {};
var choiceImagesMap = {};
var allExtractedImages = [];
try {
var pubUrl = form.getPublishedUrl() || ("https://docs.google.com/forms/d/" + form.getId() + "/viewform");
var resp = UrlFetchApp.fetch(pubUrl, { muteHttpExceptions: true, followRedirects: true });
if (resp.getResponseCode() === 200) {
var match = resp.getContentText().match(/FB_PUBLIC_LOAD_DATA_\s*=\s*(.*?);\s*<\/script>/s);
if (match && match[1]) {
var rawData = JSON.parse(match[1]);
if (rawData && rawData[1] && Array.isArray(rawData[1][1])) {
rawData[1][1].forEach(function(pi) {
var pId = String(pi[0] || "");
var pTitle = (pi[1] || "").trim();
var pImgs = extractImagesFromNode(pi);
if (pImgs.length > 0) {
if (pId) imageMapById[pId] = pImgs[0];
if (pTitle) imageMapByTitle[pTitle] = pImgs[0];
pImgs.forEach(function(u) { if (allExtractedImages.indexOf(u) === -1) allExtractedImages.push(u); });
}
if (pi[4] && pi[4][0] && Array.isArray(pi[4][0][1])) {
pi[4][0][1].forEach(function(cNode, cIdx) {
var cImgs = extractImagesFromNode(cNode);
if (cImgs.length > 0) {
choiceImagesMap[pId + "_" + cIdx] = cImgs[0];
if (pTitle) choiceImagesMap[pTitle + "_" + cIdx] = cImgs[0];
}
});
}
});
}
}
}
} catch(errPub) {}
var questions = [];
var qIndex = 1;
var pendingImg = null;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var itemType = item.getType();
var itemId = String(item.getId());
var title = (item.getTitle() || "").trim();
var help = (item.getHelpText() || "").trim();
if (itemType === FormApp.ItemType.IMAGE) {
try {
var imgBlob = item.asImageItem().getImage();
if (imgBlob) {
var bytes = imgBlob.getBytes();
if (bytes.length < 250 * 1024) {
pendingImg = "data:" + (imgBlob.getContentType() || "image/jpeg") + ";base64," + Utilities.base64Encode(bytes);
} else {
try {
var driveFile = DriveApp.createFile(imgBlob);
driveFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
pendingImg = "https://drive.google.com/thumbnail?id=" + driveFile.getId() + "&sz=w1200";
} catch(eDr) {
pendingImg = "data:" + (imgBlob.getContentType() || "image/jpeg") + ";base64," + Utilities.base64Encode(bytes);
}
}
}
} catch(e) {}
continue;
}
if (itemType === FormApp.ItemType.MULTIPLE_CHOICE) {
var mc = item.asMultipleChoiceItem();
var choices = mc.getChoices().map(function(c, idx) {
var cVal = c.getValue();
var cImg = choiceImagesMap[itemId + "_" + idx] || choiceImagesMap[title + "_" + idx] || extractDriveImage(cVal);
return { key: String.fromCharCode(65 + idx), text: cVal, imageUrl: cImg };
});
var qImg = pendingImg || imageMapById[itemId] || imageMapByTitle[title] || extractDriveImage(title) || extractDriveImage(help);
if (!qImg && allExtractedImages.length > 0 && (qIndex - 1 < allExtractedImages.length)) {
qImg = allExtractedImages[qIndex - 1];
}
pendingImg = null;
questions.push({
id: itemId,
number: qIndex++,
type: "multiple_choice",
title: title,
helpText: help,
imageUrl: qImg,
choices: choices
});
} else if (itemType === FormApp.ItemType.CHECKBOX) {
var cb = item.asCheckboxItem();
var cbChoices = cb.getChoices().map(function(c, idx) {
var cVal = c.getValue();
return { key: String.fromCharCode(65 + idx), text: cVal, imageUrl: extractDriveImage(cVal) };
});
var cbImg = pendingImg || imageMapById[itemId] || imageMapByTitle[title] || extractDriveImage(title) || extractDriveImage(help);
pendingImg = null;
questions.push({ id: itemId, number: qIndex++, type: "checkbox", title: title, helpText: help, imageUrl: cbImg, choices: cbChoices });
}
}
return ContentService.createTextOutput(JSON.stringify({
status: "success",
data: { formId: form.getId(), title: form.getTitle(), questions: questions }
})).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: err.toString() })).setMimeType(ContentService.MimeType.JSON);
}
}
function extractImagesFromNode(node) {
var images = [];
function walk(curr) {
if (!curr) return;
if (typeof curr === 'string') {
var s = curr.trim();
if ((s.indexOf('googleusercontent.com') !== -1 || s.indexOf('ggpht.com') !== -1 || s.indexOf('drive.google.com') !== -1) && s.indexOf('http') === 0 && images.indexOf(s) === -1) {
images.push(s);
}
} else if (Array.isArray(curr)) {
for (var i = 0; i < curr.length; i++) walk(curr[i]);
} else if (typeof curr === 'object') {
for (var k in curr) walk(curr[k]);
}
}
walk(node);
return images;
}
function extractDriveImage(str) {
if (!str) return null;
var m = str.match(/(?:drive\.google\.com\/(?:file\/d\/|open\?id=|uc\?id=)|lh3\.googleusercontent\.com\/d\/)([a-zA-Z0-9_-]+)/i);
if (m && m[1]) return "https://drive.google.com/thumbnail?id=" + m[1] + "&sz=w1200";
if (str.indexOf('data:image/') === 0) return str;
var mImg = str.match(/https?:\/\/[^\s"']+\.(?:png|jpg|jpeg|gif|webp|svg)(\?[^\s"']*)?/i);
return mImg ? mImg[0] : null;
}
function doPost(e) {
try {
var form = FormApp.getActiveForm();
var data = JSON.parse(e.postData.contents);
var formResponse = form.createResponse();
var items = form.getItems();
var answers = data.answers || {};
for (var i = 0; i < items.length; i++) {
var item = items[i];
var itemId = String(item.getId());
var ans = answers[itemId];
if (ans && item.getType() === FormApp.ItemType.MULTIPLE_CHOICE) {
var mc = item.asMultipleChoiceItem();
var choices = mc.getChoices();
var text = ans;
if (ans.length === 1 && ans >= 'A' && ans <= 'Z') {
var idx = ans.charCodeAt(0) - 65;
if (choices[idx]) text = choices[idx].getValue();
}
formResponse.withItemResponse(mc.createResponse(text));
}
}
formResponse.submit();
return ContentService.createTextOutput(JSON.stringify({ status: "success" })).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: err.toString() })).setMimeType(ContentService.MimeType.JSON);
}
}
Kode lengkap versi detail beserta dukungan gambar dan teks bacaan juga tersimpan di file folder proyek Anda: google-apps-script/Code.gs.
Cadangkan & Pulihkan Data (Backup & Restore)
Simpan seluruh pengaturan jadwal dan sekolah ke file JSON atau pulihkan ke komputer lain
Cadangkan Data (Export)
Unduh seluruh daftar ujian, token, dan konfigurasi sekolah menjadi satu file data JSON.
Pulihkan Data (Import)
Pilih file cadangan JSON yang pernah diunduh sebelumnya untuk mengembalikan seluruh pengaturan.
Setel Ulang ke Pengaturan Awal (Reset)
Tindakan ini akan mengembalikan data jadwal ujian dan konfigurasi ke contoh bawaan sistem.
Tambah Jadwal Ujian
Kelola Butir Soal: -
Tambah, edit, atau hapus butir pertanyaan ujian secara langsung