Pattern Cards
+overview
A "Pattern" defines a group of cards to which settings may be attached.
"Pattern" is actually not a new cardtype, but it is defined (and named) by a search card. For example, I can make a Search card called "Basic cards" (or use the built-in Basic+*type cards), and then I've got a new pattern of cards to which I can attach settings. For example, I could create "Basic cards+*table of contents" to turn the table of contents on or off for all Basic cards.
All active patterns are represented as search cards, but not all search cards can be used as patterns. This is because to use a pattern we need to be able not only to start with a Pattern and find all the cards that match that pattern but also to start with a card and find all the patterns it matches. (And that has to be fast!) Some searches, like matching regular expressions, say, will not be "patternable". In fact, it's likely that initially only a small subset of searches will be patternable, but that percentage will grow over time.
Since a given card can match many patterns, and those patterns might have conflicting settings, we have to figure out which patterns have precedence. Whether to base this on a fixed (based on which type of pattern you have) or flexible (based on configuration) ordering system is the subject of ongoing discussion.
+needs
Needs
-
We are increasingly finding our permissioning system meeting significant limits. For example:
- you can set global permissions determining who can change card permissions. Whoever can do that can change all the permissions on all the cards he/she can see. so there's no emergent areas controlled by subgroups
- because all permissions are set on a card by card basis, it's difficult to change permissions for a group of cards at once, like all cards of a given type.
- because permissions are not card content themselves, they cannot be viewed and reviewed in all the flexible ways we want, so that if you want a special admin view of permissions, we have to build a special new interface. To date this has also kept permissions out of WQL
- there are crucial permission patterns we cannot yet support, like opening up viewing of a card to an individual user or a few individual users.
- Our formatting system is pushing up against boundaries, too, including:
- lots of naming challenges. We need different names for "tforms" and "rforms".
- 2-card patterns: we would need yet another name if we ever wanted to do new formats, like "name+X" or "name+type", both of which could be handy
- 3-card patterns: On Hooze.org we're wanting to apply automated formats to things like Timberland+Climate Counts+data, but there's no way to format "Company+Stream+data" cards. Or for that matter, "Company+Climate Counts+data" cards, which might be formatted differently from other streams. And if there were, would that be a tform or an rform? I predict Meyer is going to hit this very soon, too, in virtual cards coming from Place+Topic.
- 1-card patterns -- we often want cards not to have to follow their format. For example, on wagn.org, I'd like to format all the cardtype cards, but I haven't because some, like Ticket, are special and I don't want them formatted that way.
- Other areas seem to be coming up where we want to follow similar patterns
- We're struggling to figure out what to call *edit and *new cards, since they too might apply only to type-formatted or name-formatted cards. The nomenclature got so awkward we considered getting rid of "*new" cards so as not to have so many.
- still in the *edit arena, we're finding overapplication of this pattern to 1-card scenarios. *edit instructions intended for X+A cards are showing up when editing A
- we've wondered if we won't run across similar issues for *options, *limit, etc.
+solution
Solution
We propose to abstract the idea of a "pattern" out from these separate problems -- conceptually, in card structure, and in code. The basic idea is that we should be able, whenever applying a behavior (edit cues, formatting, etc) to cards following a given pattern, to specify that pattern precisely and independently -- that is, without having to bind, say, editing behaviors to permissioning behaviors. Let's start by looking at the common case we have now where we want to talk about all the cards of a certain type, say "User". Currently we'd just attach things to the User card itself, as with User+*tform and User+*edit. These two examples illustrate two subpar solutions to the same problem with our current system: poor pattern identification. With *tform we choose to identify the pattern in the star card, which means we've had to create *tform and *rform. To push this solution further, we'd have to create more and more star cards to support more patterns. The *edit solution is really avoidance. It doesn't differentiate, so there is no way to change *edit cues for User cards without also changing it for cards ending in +User. The simplest solution we've found is to have a card that identifies each pattern separately: one card that represents all the user cards, and another card that identifies all the cards ending in +user. Conveniently enough, we already have those cards! User+*type cards is a search that finds all cards of type user. Whether that's the ideal nomenclature is debatable but not the point. The point is that a search defines the pattern. In essence, it is the pattern. Once you have the pattern, you can attach the pattern-shaping star card to that pattern card (the search card). So, for example, formatting for all User cards might be set on User+*type cards+*form (instead of User+*tform), and editing cues for all those cards might be set using User+*type cards+*edit. And now we've solved the problem of what to name *edit and *new instructions that are only meant to apply to user cards --> eg. User+*type cards+*edit
Benefits
What does that get us? First, it allows us to apply patterned behavior to a lot more things. Among our current star cards, good candidates for patterns include form cards (*tform, *rform, which may become just *form or *format), *edit, *new, *table of contents, *options, *watchers. We can build patterned layouts, including expanded definitions of things like *sidebar, *logo, *favicon, etc. And, perhaps most importantly, we can set up patterned permissions (see separate section below).
Second, we can create a much richer and more nuanced array of patterns. Currently we have a primitive form of support for a few proto-patterns: "type", "card+name", and "type+type" (not sure whether "type+name" is actually working?). Now, we add a vast array of WQL-able patterns.
That said, though all patterns are searches, not all searches can be patterns, at least initially. A search including a "match" operator, for example, would not be "patternable", because as explored in the implementation section below, it is important that we not only be able to start with a pattern and find all the cards that conform to it (which basically means running the WQL query) -- we must also be able to start with a card and find all the patterns to which it conforms. The match operator complicates this. For starters, there will probably a limited ok-set (a white list) of patternable WQL operators and properties. These are very likely to include type, name, right, and left. The initial list may not be much larger than that but may grow as the pattern mechanism matures.
Special Cases
We will also need a clear mechanism for searching for "self" patterns that allow us to make isolated settings on a single card. On the other extreme, we will also likely wanted a *all or *default card that is used to set site defaults. And we'll definitely want a "star card" pattern, one way or another. Will be helpful to look at "initial permissions" page -- we'll certainly need to represent all those patterns for our permissions.
Overlapping Patterns
Many cards will meet multiple patterns. If, for example George+wishes is a Basic card, and there are pattern cards for both Basic cards and cards ending in +wishes, which pattern will it follow? The first point to make is that most patterns won't have all settings. Just because there is a Basic card pattern does not mean that there is a *form setting for Basic cards. For there to be a conflict to resolve, there must be multiple setting values. So it's a setting conflict, not a pattern conflict per se. But let's assume that we have a setting conflict. How do we choose? Our current solution is Pattern Precedence. How exactly to represent this precedence is not yet spec'd, but the idea is that all patterns on a given Wagn can be ranked from weakest to strongest. Something like *all or *default will be the weakest (though likely most prevalent) pattern on a Wagn, whereas the narrower patterns will generally be "stronger" (though less prevalent), in that they take precedent. So our current example, George+wishes, may actually conform to 3 patterns: *all, Basic cards, and cards ending in +wishes (all 3 of which would be represented with searches), which for now we'll call A, B, and C. Lets say we'd ranked them from weakest to strongest in that same order (A, B, C). Then, if there were a *edit setting card for the +wishes pattern, that's the card that would apply -- it's the strongest pattern. If not, then a *edit setting card for Basic cards would rule. Lacking that, it goes to *all. Here are some things to consider when spec'ing our pattern precedence:
- we don't want to have to fiddle with precedence every time we create a pattern. we should usually be able to get away with some default behavior (probably rules out a pointer rep)
- we would like to have certain patterns firmly planted at the weak and the strong extreme. *all should be built-in to be very weak. *itself (or *self or *alone or whatever) should be built-in to be the strongest pattern, overriding everything else.
- eventually we'd love to have a simple, probably drag and drop GUI for resorting pattern precedence
This likely means that precedence is done via a search that sorts first by assigned precedence (probably a plus card on a pattern) and second by some implicit, constant indicator (like id or create date, both of which should generally yield the same order except in cases of specially imported data).