Skip to main content

Conference Session

Drupal LMS: A new, modern, easy-to-use Learning Management System

September 26, 2024
Photo of Marcin Grabias

Marcin Grabias

Senior Drupal Engineer

Schools, public organizations, and individual teachers need a reliable, low-cost way to run online learning, and the existing Drupal option carries years of procedural code that makes it hard to maintain. Drupal LMS is a clean rewrite: a single module of object-oriented code, built on the Group module, that aims to be the core of an open source learning ecosystem on drupal.org. This session shows what it does today and where it needs help, which matters for any team that has ever wrestled with an LMS on Drupal.

Session Description

Learning management systems on Drupal have long meant one thing, and that thing carries a lot of history. This session introduces an alternative built from a clean slate.

Marcin Grabias (Senior Drupal Engineer) tells the story of Drupal LMS: how a client project built on Opigno grew a long list of patches, why he and the team eventually forked, and how that fork became a fresh "clean" branch of new, object-oriented code. He explains the data model, activities, activity types, answers, lessons, courses, and their statuses, all built on the Group module, and the plugin API that lets developers add new answer types. A recorded demo walks through building an activity type, a lesson, and a course, and taking one as a student, including the grading and revisit flow.

Marcin is candid about what is not done yet: notifications, PDF certificates, more activity types, UI improvements, and the eventual migration from Opigno that will bring real data. He is joined in the room by colleagues from Tag1 and by community members already contributing, and the pitch is straightforward: this is open source, it is a foundation to build on, and feedback is what moves it forward.

What You Will Learn

  • Why the team forked Opigno and rebuilt a Drupal learning management system from clean code
  • How the Drupal LMS data model works: activities, activity types, answers, lessons, courses, and statuses
  • How the activity answer plugin system connects a presentation layer to answer widgets
  • What the module does today, from course navigation to teacher grading and a revisit mode for students
  • What still needs building, from notifications and certificates to an Opigno migration
  • How the project is positioned as the core of an open source LMS ecosystem on drupal.org

Transcript

[00:00:02] Hello everyone, can you hear me okay? Nice. Thank you for coming. I came here to tell you about my open source project. It's a Drupal learning management system. So far it's a non-paid project. I decided to do it because I think the Drupal community and public organizations like schools, and students, deserve a reliable, low-cost solution to implement online learning in today's times. Okay, let's go.

[00:00:53] First I'll tell you about the key actors, then how it all happened. I'll describe the problem of the initial project, which was opinion-based, then give an overview of Drupal LMS, we'll have a demonstration, and I'll tell you about the work that still needs to be done. Hopefully we can have a small discussion.

[00:01:30] The key actors. There's me, Marcin Grabias. That's how I look on drupal.org. I'm a chemical engineer and, of course, a Drupal engineer. There's Nat, a core maintainer, core committer, and probably the best reviewer I've ever met. There's also Andrei Mateescu. Andrei is mostly a backup, so if Nat and I disappear in some strange situation, Andrei has an underground shelter and can continue the work. And that's how we look in reality.

[00:02:20] There's also the Tag1 team, which has supported the project from the very beginning, and our clients, AFL-CIO and the American Federation of Teachers, who have also been very supportive and understanding about me not focusing that much on the current project and a bit more on Drupal LMS, which will be implemented. I think the plan is February, so we need a migration matrix, but more about that later. There's also the Drupal community, which is slowly starting to get involved, and this young guy helping with testing and documentation, Arthur. If we have a teenager who wants to get involved, then the project has a future.

[00:03:24] Now, how it all happened. The Opigno project at Tag1 started in 2019. It was a bit problematic from the very beginning, and the list of patches grew longer. At some point, I think before summer 2022, so two years ago, we had a meeting with the Opigno team to discuss the status of patches. It was a nice meeting, and we came to an agreement that they would review our patches by the end of September. Unfortunately, nothing like this happened.

[00:04:12] We decided to create a private fork. I checked in the repository, and it was September 2022, so that's when we actually forked Opigno, two years ago. In 2024, around April or May, we made the fork public, and it only consisted of Opigno code and a bit of our custom code.

[00:04:52] Since the fork was already public, I thought we should give something new to the community, and not just all this long code with a bit of our rewrites. So I created a clean 1.0.x branch, which consists only of clean, new code, and it's been developed since May this year. We had our first alpha release in mid-September.

[00:05:40] Now a few words about the primary LMS, which is Opigno. My sources are publicly available, so you can check these facts. Opigno started in 2008, which is 16 years ago, so that's quite some experience. Its reported usage, according to opigno.org, is 9,000 organizations, and drupal.org usage is 2,000 installs. That's a bit of a difference, but maybe some usage is just hidden from drupal.org.

[00:06:24] The Drupal 8 version launched in mid-2018, and Drupal 8 was released at the end of 2015. If some of you remember, those were hard times for Drupal development. Drupal 8 was not accepted yet, and projects back then really lacked in quality. So I think the Drupal 8 version launch was very early for such a big project.

[00:07:01] Opigno contains thousands and thousands of lines of procedural code. A lot of it has probably just been moved from the Drupal 7 version and not rewritten. It has data duplication, code duplication, and unclear dependencies, because it has a lot of modules with code references to each other, so you never know what depends on what. By the way, how many of you are developers? Awesome, almost everyone. Those who aren't, you can ask the developers later, or just raise your hand if you don't understand and I'll try to be clear.

[00:07:59] Opigno also has company-controlled releases. If we post a patch on drupal.org, we never know when it's going to be released, and the commit is not going to be there. Maybe the changed code makes its way to the next release, maybe not, but the commit won't be there, and we never know. They have an external repository beyond the Drupal code. And Opigno has no or minimal caching, so this affects performance a lot. There's been an open invitation to fork it since May this year. But as I said, it's still a huge, solid base of experience and data structures that we learned on while creating the fork and the clean branch.

[00:09:09] So what were the inspirations for Drupal LMS? The first was Opigno, because the initial project was based on it. Then public education systems, schools and universities, because the plan is to create a learning management system where we can build a school with classes, lessons, teachers, managers, and students. Also some learning apps: I sometimes use Duolingo and Udemy. And our clients, who give a lot of feedback all the time.

[00:09:56] Now, the definitions. The data structure of Drupal LMS is a simplified version of Opigno's, without duplication and with a lot of cleanup. From the smallest unit, we have an activity, which is a question widget with a presentation layer and some fields. We have an activity type, a bundle of an activity, which correlates activities with answer plugins and lets us configure the presentation layer, adding fields and configuring display.

[00:10:41] We have answers, which store user answer data and calculated scores. They're editable only via an answer plugin, but also when a teacher grades an activity that needs manual evaluation. We have lessons, which are sets of activities with some additional parameters I'll show later, and lesson statuses, which are basically the same as answers but at a higher level.

[00:11:24] We have courses. I forgot to mention, Drupal LMS is built on the Group module, so a course is a group bundle. It has a presentation layer, references to lessons, and some additional settings. And we have a course status, which is again like lesson status but at a higher level.

[00:11:50] The plan is to make Drupal LMS the core of an LMS ecosystem on drupal.org. It's a single module, it contains clean object-oriented code, it uses PHP attributes, it works with the latest version of the Group module, it has Symfony autowiring, and it's Drupal 11 ready, once the Group module is Drupal 11 ready.

[00:12:31] Let's talk about the API a bit. First, we have activity answer plugins and their API, which lets developers create additional plugins, and we need a few more of those. We have LMS entities, which I described earlier, some Views integrations, core entity hooks, and additional LMS events, which can all be used as part of the API. That allows people to create other ecosystem modules.

[00:13:16] Now the core functionalities that we currently have in the latest version. We have course, lesson, and activity operations: create, read, update, delete. We have an extended reference field for course lessons and lesson activities, which stores a reference to the entity plus additional parameters. We have student management in courses and classes. We have course navigation logic, which controls which activity is next, whether you can go to an arbitrary activity, whether you can go back, and which lesson is next.

[00:14:06] We have presentation of results already, and evaluation of answers, like open text, that need teacher evaluation. There's also a simple permission system that will probably be extended; it's very simple now, but I think it's suitable for schools already. And we have activity types.

[00:14:35] Now about the activity answer plugin system. It connects the presentation layer of activities with the answer widget, a form element where the student can give their answer. We can build unlimited combinations of activity types and answer plugins: a slide with a true/false question, some text with multiple choice, or just an informational activity where the user reads and clicks submit.

[00:15:23] Given the available answer plugins, an answer plugin controls the answer form elements, controls whether the evaluation is automatic or manual, and in the case of an automatic one, calculates the score. It controls the display of the evaluation form, and it contains installation procedures with a simplified API that creates the fields on the activity needed for displaying the form.

[00:16:16] Now let's get to the demonstration. Usually I do a live demo, but Dries inspired me in his keynote, so I recorded it. We have a Kickstarter project that has a Composer command that installs our project. It's an additional repository, linked on the Drupal LMS project site, and it also creates QA content, which is apparently the same content used in the automated tests. So we have a preconfigured environment with some example content installed already. It takes just a moment.

[00:17:31] The QA content is created, and we can log into the page. First we'll log in as the LMS admin user, one of the preconfigured roles, and users are also created. First I'll show you how to create an activity type. We already have some created as part of the test content. We specify a name, the machine name is determined automatically, we select an answer plugin and a default maximum score, so we don't have to enter it on every activity in the lesson, though we can change it later. And it's created: a slide with a free-text answer plugin.

[00:18:33] Now we can add some fields to it. Since we named it a slide, we need to add an image field. I just disabled the alt field to save time figuring out alt, because I don't have AI to do it for me yet. We can configure the display and the form display, and we can even use Layout Builder if we install it, so it's quite powerful.

[00:19:15] That's all we did as an LMS admin. Now let's get to the teacher functionality. All the passwords are 123456 on the QA content, which is quite convenient. As a teacher, there are already some activities as part of the QA content, but we'll create one of the type we just created. We have a question field, which is required, so we need to display a question, otherwise it wouldn't make sense in this activity type. And we're adding an image. That worked.

[00:20:27] Now when we're editing a lesson, there are some additional parameters. We can randomize the activities, set a custom order, add additional activities. This widget will need to be reworked a bit so it has a better user experience; I'm planning a draggable table and a modal where we can add and create additional activities.

[00:21:06] Now the course, where we specify lessons, some of them mandatory or not, and set a minimum success score. There's also a revisit mode, where students can always go back to the course, even if they failed, and correct their answers.

[00:21:24] Now the student part. We have lessons, activities, and courses, so we can start learning. This course is publicly available, anyone can join it, and the student just joined a default class. There can be multiple classes, but in this case a teacher or manager decides which class the student is added to.

[00:21:57] I wanted to fail the course to show you the revisit mode, but the revisit mode is available even if we pass the course. Now that's our newly created activity type, where we have a slide and a question. Maybe the question should be below the slide, for me. And an open-text answer. This is a no-answer activity where we don't have a widget. Another open text, that's the second lesson already.

[00:22:40] So the course is completed, but it has open-text answers, so it needs grading. Now we should log in as the teacher again, check the results, and evaluate the answers, score them. There are still things to do here in terms of the user interface, like tabs and breadcrumbs. It doesn't affect the LMS score that much. So we score in a modal, but it has a Node.js option, so the scoring form can be implemented elsewhere as well.

[00:23:40] I wanted to make it fail this time, so the first lesson is failed and the second is passed, but all mandatory lessons need to be passed. Okay, now the revisit mode. Here's one thing: I didn't take two takes while recording this, and I did it on purpose, because there's something that can be corrected but I still have to figure it out. I answered one activity and couldn't go back, because the course needs grading again since I finished the last activity. When we go to the revisit, we always land on the activity we just visited, which for a finished course is always the last one. So probably a save button there instead of "next," or the submit button should say "finish course."

[00:24:45] So I had to log in as the teacher, evaluate again, go back to the student, and then go back to the right activity that needs correction. Hopefully it won't take too much time. I think we're good with time. As a student, I'm correcting the answers now. All good. And now re-evaluation as the teacher.

[00:25:50] This is all using the Drupal AJAX API and Form API, by the way, so there's basically zero additional JavaScript here, only the Drupal core JavaScript. So now the course is passed, and the student can also view the results. That's it for the demonstration.

[00:26:24] Now, what still needs to be done? Quite a lot. We'll need notifications: if a student requests membership on a course, the teacher gets a notification; if a class is added to a course, the students should be notified that they have one more course available. There are quite a lot of those. We're planning a notification block with a bell and a number of new notifications, plus optional email notifications.

[00:27:15] PDF certificates, that doesn't need additional comments. Activity types: since Opigno used those three, our clients are very used to them, so we'll need to implement them. Hopefully other companies working on Opigno who'll need to migrate eventually will help with that. Those are hugely JavaScript-based, and their modules download external JavaScript libraries to the site. I don't like them, but we'll need them.

[00:28:01] User interface improvements will be needed. I told you about the lessons widget and the activities widget, but there's more, including probably some theme work. Performance improvements and big data tests, that may be after the migration, when we'll actually have more data. And the big task that will give us that data is the migration from Opigno.

[00:28:45] Now, are there any other options? I just saw a presentation of an LMS which is interesting in some terms, because it's much more JavaScript-based, it uses React. So if someone is a React developer, that may be a solution. But three things: not a single custom Drupal entity, a lot of configuration, and a lot of JavaScript. Those data structures available in Drupal LMS, I think eventually, if that LMS grows, they'll be needed there as well, so hopefully we can join forces at some point.

[00:29:42] And there's Opigno, who has experience with Opigno, by the way. Okay, not that much. They're supposed to release a Drupal 10 compatible version at the end of September, so that's some news. But I tried to clean up Opigno and found it close to impossible, because every method I tried to remove had references, and those references had references, and finally some deep reference was actually used somewhere in our code. Spending time tracking those references, I think the Drupal 10 compatible release will still contain thousands and thousands of lines of procedural code, which is really hard to track and makes working on it hard.

[00:30:43] So Drupal LMS is modern and clean. There's no spaghetti, no procedural code, all organized. It uses well-known solutions from Drupal core. There are no dependency patches needed so far, well, one for Group, so we can run Views Bulk Operations on some entities, because there's an error where Group's view permissions don't work well with VBO. That's just one small patch, and hopefully I'll be able to remove it soon with some work on the Group and VBO modules.

[00:31:38] It's supported by Tag1 Consulting, which is a great company, I have a teacher t-shirt here, I have three actually, and it's based on solid knowledge and experience, since we worked on our projects for AFL-CIO and the American Federation of Teachers for five years, plus what's in Opigno that we're based on.

[00:32:17] It's also extendable. I described the API a bit; it's the heart of an ecosystem where there's a lot of room for other module developers to contribute and create a place of their own that works with Drupal LMS. And it's 100% open source, and always will be.

[00:32:45] Now what are the plans for a beta and stable release? If it depended on me, well, it does, but still, I'm missing feedback. The beta criterion is usually that the API is stable and there isn't much API usage, so if I have better feedback from other developers, that it's good or bad or there's something to improve, we can either work on it or release a beta. Or if there's more usage of the alpha version reported on drupal.org. The community is still mostly watching and waiting, probably for the beta and stable release, but it actually depends on them.

[00:33:44] Every little contribution is a step forward, sometimes a very big one. For example, Claudio spent, I think, one day on the project. He did some tasks, but thanks to him we have Symfony autowiring everywhere, which is a big step forward. Always with a second and third pair of eyes, and with community work and feedback, we can make the project many times better than it would be without it. So everyone is invited to collaborate. That seems to be all. You have the drupal.org link to the LMS project here, and a link to the Kickstarter repository if you just want to install it; it depends on the dev release. Thank you.

[00:35:03] (Question from the audience.) Thanks for the presentation and the contribution. We worked quite a bit on LMS. Once we took the initiative on a project, Open LMS, but we couldn't continue, so I'm glad you're doing it. One of the bottlenecks we found, and why we later moved more into decoupled systems that could take more load and scale in the back end, is when concrete load comes. One example you showed, taking lessons, quizzes, is a good one: most of the time everyone starts at the same time, 10 a.m., everybody's there. How do you plan to handle that kind of scale or load, apart from the good caching system that differentiates it from Opigno?

[00:36:32] I don't know, it's just that every time I work on something I keep performance in mind, so I can tell you the code is optimal, but I don't think there's another solution. We cache elements of the site that can be cached, like the navigation block on the right side, which is totally not cacheable on Opigno. So, as much caching as possible. Other than that, we can't overcome some things, especially with a quiz, because you're storing attempts, so it's a lot of database inserts. The bottleneck with the database we did handle with Aurora autoscale, but if we run into problems, instead of storing the data in the database we can temporarily store it in a cookie on the client side, which would improve performance. Maybe that can be a follow-up.

[00:37:45] (Question from the audience.) This looks like a really good candidate for recipes. Are you using them, and if not, do you plan to? Not yet. There are some install tasks in the Kickstarter repository that do all the things, but we plan to create a recipe in place of all that config. There isn't a lot of config actually, but it's a very raw site, as you saw in the demo.

[00:38:34] (Question from the audience.) Thank you for a good session. I also work on an LMS based on Opigno, and we have a challenge to make mobile integration. You mentioned Udemy, and they have a mobile version. Do you plan some kind of API, like a REST API, possibly a submodule that opens those endpoints with OAuth authentication? That would be very good. Yes, you can just create an issue on drupal.org and I'll probably follow up sooner or later. That's also why I didn't like those three activity types I mentioned, because they're not mobile-ready or responsive. That's why I'd like to focus on everything that can be created using the Drupal Field API.

[00:39:46] (Question from the audience, continued.) In another project we have mobile integration, and the problem is that when we use, for example, Form API for some validation or submission logic, when we go to an endpoint we don't have a form, we only have JSON. So it's not logical, on the back end, to build and submit a form programmatically from JSON. We had the idea to move our logic to separate services. Do you think about that kind of implementation, to separate the forms from the calculation logic that detects the right answer and the score? It is quite separated, actually. We have a training manager service that calculates which activities should be next, where to start, and whether they can go somewhere. Those are all backend methods, so it will be relatively easy to implement an API, I think.

[00:41:08] (Comment from the audience.) This is cheating because it's not a question, but I work with Marcin. I just wanted to come back on the performance question. A big change in Drupal core landed in 10.3: we can now render-cache POST requests. Before 10.3, when you submit a form, everything would render, including the form, and then you'd get redirected. Now only the form itself renders before you get redirected, so POST requests just became a lot lighter. If you have a site with a lot of content entry happening simultaneously, it's a lot lighter than it used to be. There's also scope for the forms themselves: they haven't been render-cacheable, but you can now opt in to render-caching for form rendering itself, except when it's submitted. So it's the last frontier of Drupal caching, now open to optimize, and it will be a lot easier to handle very heavy content-entry websites.

[00:42:43] (Question from the audience.) A question about your vision: do you want this to be a product, or a framework? I don't think so, as a product. As I said, I wanted public organizations, and maybe individual teachers, to have a cheap solution to create something, maybe with the help of a site builder who has Drupal experience. There are no plans to make it a product with subscription payments. Hopefully it'll never go that way.

[00:43:31] (Question from the audience.) Thank you for the session. Are your clients asking for AI features? No, we haven't heard anything like this yet. Awesome.

Event Details

Conference
DrupalCon Europe
Date
September 26, 2024
Location
Barcelona, Spain
Skill Level
Intermediate

Work With Tag1

Be in Capable Digital Hands

Gain confidence and clarity with expert guidance that turns complex technical decisions into clear, informed choices—without the uncertainty.