The brand new site is up and finally looking like it's supposed to!
It took a really long time this time around, mainly because I did end up starting 2 new projects: Laravel Drop-In CMS (DiCMS), and DiCMS-Blogger. I started new pages for these two new projects, but they only contain the github repo link, as I haven't had the mental will to type them up. Much easier to type code than type words.
Regardless, the two projects are up and running and, although they need work, they're functional enough to run this site and blog for now, so it's a win. Also I noticed that my content was getting wiped, even with the backup I had set up. I gave up and worked on the backup system for DiCMS instead and I'm proud to say it works great. I have only 2/3 features built right now, backup through the website and programmatically, artisan is yet to come and, now that I think about it, and API way should be done as well.
Now I do plan on mainly working mainly on my LMS, but do want to release a 1.0 of my other 2 projects sooner rather than later, so I've decided to split my time 60% LMS and 40% CMS. The way I plan on going about this is to mimic it on my goals for the next blog post.
Before that, though, lets go over what has been achieved!
Publish the sample env and yalm files.
env has been published, yaml has not. Mainly because I didn't think it was needed.
refactor the person object into the above
Done!
Add the ability to view all people in the system (this will eventually be deprecated, just a jump of point for now)
Not exactly. Search is implemented, but I'm holding off on the roster. More below.
Add the ability to create new people
Not yet.
Include the start of a duplication detection system that prompts users before adding people.
Nope.
Add a very basic person management system that will allow someone to edit basic information and maybe assign some basic roles and permissions.
Partly. Edit basic info is there. Roles/permissions are not. More info why below.
Add and test soft deleting people.
Nope.
Add a way to merge people. Include a way to expand it with new systems
Nope
Create a new Git project for the drop-in CMS
Yes!
Learn and start authoring a Laravel package
Yes! authored and published 2!
Integrate it as a "replacement" in my existing environment.
Yes! Currently writing this post on the new, integrated system.
Now thinking ahead I do want to concentrate the most on the LMS system. I did, however, create a non-complete list of all the features I want in my other two projects, so I will be picking some items from there and work on them until my next blog post. However, most of the post will be about my LMS. So as you can see it doesn't seem like I did much work on the LMS, which is partially true.
The fact is that implementing the Person object simply led to too many other questions that had to be answered before I continued. First one was, why use "look up" tables for a lot of the fields in the Person object. I struggled a lot with this, mainly because it seemed so easy to simply code them in a much less database and processing-resource-hog way. The fact is that having to pull an extra 5-10 queries when getting a Person object might be too much. Why not make them text fields? Or even better make the whole damn thing a JSON array and store whatever the hell I want. It might still go to that, because I'm not 100% convinced that I'm going the right way. My rationale for this is the following: the fields that I picked have to be standardized. This is not an aesthetic thing, schools, especially private ones, need accurate demographic information that they can report to the state to help them get grants and other goodies. Some schools have mandates on diversity, some are really rigid about gender roles, some are not. The point being is that certain fields must be able to be standardized by the school. Ethnicity is probably the biggest one, but development (aka fundraising for us techies) wants titles and professional honors and salutation so they know how to talk to the person. Admissions wants gender data to make sure their numbers add up, etc. All these things can be pulled so much easier with a normalized database.
The alternative, of course, make them text fields and enforce the options in code. But then data can be messed up, or updates maybe don't work and you still get old data. It's messier. JSON has he same problems, add to that you still need another table to contain the "valid" options, so you're still using a table, just less of them, but the queries are about the same. Plus in this world of databases and speed, is it really worth saving a few queries? that are usually cached anyways? So I went the old-school way and normalized my damn database.
As a plus to this, I also decided to ditch Laravel Backpack. It's a nice package, but I just really dislike how it just creates view in the public space. In google, that means creating a bucket and uploading/downloading, and I really want to keep that interaction to the minimum. Plus, I only really needed a way to edit "tag" tables (tables with just a name and an order to display), so I instead dove into Livewire and made a component that lets me do that just. I'm actually really proud of it, and I'm really starting to like livewire, so there's 100% chance it will be used more.
That being done, I started to think about rosters. Off the bat I know I didn't want to build an index page. It seemed so pointless; a table that just show people in the system? No one looks at this. They search and look up people, they only really need tables when creating rosters, which is a much bigger thing than just an index page. So decided to implement searching instead, but there was no point in doing that until I could have a page that I could search to. So I instead started working on a working profile. I went online and found out a really good template (https://bootsnipp.com/snippets/K0ZmK#google_vignette) that I decided to tweak and make my own. It came out rather well and I tied it to my profile, which people can see under their menu. I then tied a search and began my work on editing basic information when I started thinking more about the permissions system.
While I have a good idea on how to work it, using Policies similar to the way I did in DiCMS, I wanted to put something down that I can follow and not just go at it blindly. Mainly because there are a lot of different permissions that need to be addressed just in the Person section. I created a Person policy that currently denies everything except editing your own profile and viewing your own profile. This means that user accounts can't actually see search results, but the next question is should they? Or rather should they see all the people?
This comes to visibility and privacy. All users will have addresses, but who can see what? Can teachers see parent addresses? Probably not, buy phone numbers? Maybe. We also know staff can see all the phones, but should all the staff be able to see all the addresses? especially if your school has famous people in the rosters, you might not want the secretary to access their address. Should parents be able to see each other addresses? phone numbers? what if one parent want to but another does not? and which info would only be under heavy permissions? All these permissions have to do with just viewing people, we haven't gotten to editing: can anyone edit pictures? Can we allow certain fields to be editable by users? All of these things need to be fluid and configurable. I will tell you, most school don't pay attention to this information. They set it once and forget it about it until someone throws a stick, so it is better to allow for the maximum configuration, even if complex, to stave off a lot of problems down the line.
So let's start with viewing. There needs to be a compromise between allowing people to make certain information private to other people, and for the school to mandate that certain information be kept private from certain people. So we can separate these two into two sections: public information and private information. This is not exactly what it sounds like though, public information is information that the school feels is ok for people to share freely, such as their name or, yes, even their addresses and phone numbers. This is mainly because the LMS is supposed to feel like a community and we want to make them feel welcome. If parents choose to share a phone or address to make it easier on other parents, then the school should have no issue with it. Private information is information that people don't get a say on who sees it or if they do at all. This could be sensitive such as whether a person is in financial aid, or their SSN (or drivers licenses or national ID or whatever), but it could also be their email. Meaning that the school has decided that no one gets a choice on whether their email gets displayed or not, or whether others can see it. This means that these fields need to be configurable at a school-level.
From there, we need to be able to specify how each field is viewed and by whom. Now, this can quickly de-evolve into a mess of a solution where you have to decide what permission each role has to view other roles information, which leads into a N^2 mess that no one wants. It needs to be simple but powerful so we have to take away some complexity, which means less config ability. So we agree that we need to be able to specify at a broad level what information students can see about parents, employees (that is, faculty AND staff) and other students, what information parents can see about students, employees, and other parents, and what information employees can see about students, parents and other employees. Now, keeping "employees" as staff/faculty, we come up with 9 policies, if we choose to split employees into faculty and staff we now need 16 (ouch), and if we want to separate Coaches out, we now need 25, which gets us firmly into N^2 again.
Now these are base policies, what they do is:
1) allow admin to decide what the private fields defaults for each category are. Meaning if Name is a private field, they get to choose whether all employees get to show theirs.
2) allow admins to create a suggested defaults for all the public fields that will then be pushed out to each person, but that they can then change themselves.
Now, we want to push the admins to work with 3 groups and 6 policies, or, at the most, 4 groups and 12 policies. so what we do is make it required to have 6 policies but allow the admin to add a role and specify the policy for it. Now only the 6 basic policies are required. Why? Because there's really only 3 real groups here: employees because we hire them and manage them, non-employees, which we call parents but could be friends or some other role, and students. Students are special because they're an in between; we manage a lot of their stuff but they don't really work for the school, so they're the "special" class of people. So 6 policies are required to establish a base line. From then on, we can add a extra role (from the system including custom roles) and then ask what they want to be tied to as the default. If a school chooses to separate faculty, then they would be tied to the staff role. Now here is the important part, these new "custom" role you added can only define the following:
1) What "extra" information this role can see about the other 3 roles: this overrides all their defaults, so all teachers might be able to hide their phone numbers, but the dean role has the "view phones" field turned on the staff, so they see it.
2) Extra information that other people can see or be blocked from seeing. So if you decide that all employees can hide their emails if they wish, but not faculty, they need to show theirs.
This is coarse information, of course, so it restricts the amount we can customize, but adding a new role only adds 4 policies to the system, going to a much more pleasant 4N.
Now, this is NOT what the user sees. They need even less complexity, meaning they only see a list of the public fields and they get to choose whether others (that is both employees and students) get to see their information or not. This leads to the perfect axiom that the parents are students can only block whom we, the admin, chooses.
So what we end up with is a 3-d matrix with the 3 basic roles on one axis, the other 3 basic roles in another axis, and the third axis contains:
1) Is the field mandated? Meaning do we let users override the default?
2) is the field viewable? can the roles see each other's field?
Now, if field is not mandated, we allow the user to select whether other people can see it or now. The user's view of this viewing permission is much less complicated. The user only see a list of all the public fields and is allowed to show them or not. They don't get to choose the roles only what the access is and it will then apply to the other 2 roles. This way a parent can hide their information from all employees, but admin can decide which class of employees can see that information anyways.
Now, there is a also a "people.view" permission, which will allow anyone with it to see everything. We need this for editors, who will be responsible for making sure of the integrity of data, and to be able to merge people when they're inevitably entered twice. This leads to next issue, editing. Now editing has two issues, what information do we allow users to edit for themselves and what information do we want to "section off" and give special permissions to edit?
There will have to be a "people.edit" permission, which will allow a user to have complete editing control, including to merge user accounts, but there is some information we would like to section off. So, there is no need to to have a school admin to give permissions to change employees email. This is usually handled by IT who have their own procedures and other databases that need to synched up when an internal email is changed. Likewise, we might want to let secretaries change the parents name, but not the students, as records need to be informed of it for transcript purposes, so we let someone higher up have the ability to change it. We might also let students change their own pronouns, from an accepted list, of course.
So based on all of this we need an extra set of policies. But these are far more simple. First, we need a option for every field to be self-editable, meaning we allow that field to be changed by the user. Next, we allow the admin the ability to create a "editing permission". This will be a real permission that will be created under the Person Editing category that will be attached to a list of fields that that permission can edit. So we will have the "edit all" permission (people.edit), and then you can create another permission "person_editing.basic" that only allows the basic editing of fields, and give that to the roles that need to be able to edit. They will then get an edit button on a profile that will let them edit that information only.
The final piece of this is who can search what, but that will wait till the next post, as it also deals with rosters, which will also be coming up.
So based on this, these are the goals for next post:
1) Create the viewing permissions system, including the admin view and user view.
2) Create the editing permission system and make all information editable
3) Add addresses and phone numbers to the system.
4) Make "suggested" permissions and put them in the seeder, so they always come up.
5) No adding or deleting yet, but we need to be able to merge accounts, so we need to start work on this.
This is a deceptively big list, especially the first 2 items. This is the list for my other 2 projects:
1) Beef up the text editor, I need to be able to add links and lists and other goodies.
2) Beef up Grapesjs for better layouts. Add more icons and look into the possibility of adding symbols
3) Add artisan backup command
4) Asset manager
This is a good start and I'm sure I'll come up with more stuff and will make another post if I decide on a good release. Will update when done.
One more Addendum: Need to put this here so I don't forget. Google cloud handles things weird. Originally, when I created my DiCMS code, it was setting up a link in the front page that went to a "Fake" js and css file that had all the custom css and js for the site. For some reason, the endpoint refused to load, it looked as if it could never reach those routes. I couldn't figure it out, so I went back on a design decision and pasted the contents of the css and js scripts directly on the page. However, I now notice that my livewire widgets weren't working. Upon another investigation it lead me to understand that anything that ended in a .js was being caught by this nugget in the app.yaml file:
- url: /(.*\.(gif|png|jpg|css|js|webp))$
static_files: public/\1
upload: public/.*\.(gif|png|jpg|css|js|webp)$
Which made sure that my scripts and images are being served correctly for my static files. So I needed this part above it. This is important, it matches in order, so it will first match all .js and .css scripts and try to server them statically, then it will go to the script.
- url: /livewire/.*
secure: always
redirect_http_response_code: 301
script: auto
This makes sure that anything livewire gets caught before and goes through the app, not served statically, fixing the livewire 404 error that Google Cloud was giving me. Now I can fix this in DiCMS if I wanted to, but it might just lead to more headaches in other systems, so I'll have to think about it.