Problem/Motivation

There exists a proposal to adopt CommonMark spec as the "standard" for MarkDown at Drupal.org.

As a response to this proposal, Mark Carver created the CommonMark project.

This project has adopted CommonMark for the Drupal 8 version (see #2933713: Support league/commonmark 0.x and 1.x), while the Drupal 7 version is based upon the legacy Gruber Markdown library.

These two versions are not fully compatible.

The Drupal principles encourage:

Collaboration: Support open, cooperative information sharing systems and approaches

I believe that there is no need for two alternative Markdown modules in the Drupal ecosystem. The people behind the two projects should collaborate on a unified Markdown project. I believe that the future for Markdown is the CommonMark spec, so CommonMark should be the foundation for the unified Markdown project.

Proposed resolution

The two markdown projects should be merged.

Remaining tasks

  • Make sure the Markdown filter project has an active maintainer.
  • Reach out to Mark Carver to initiate collaboration.
  • Make CommonMark the official/supported markdown PHP library
  • Merge in the Drupal 7 code from the CommonMark project to make both the Drupal 7 and Drupal 8 version based upon CommonMark.
  • Make sure this module has the latest and greatest version of the ComonMark library for both the Drupal 7 and Drupal 8 version.
  • Make sure the result of the merging process can serve as the canonical Markdown filter project for both Drupal 7 and Drupal 8.
  • Add previously supported parsers (libraries) back in as proper plugins.
  • Add Twig extensions
  • Discuss/figure out upgrade path? (not needed, see #28)
  • Add necessary config
  • Move https://commonmark.unicorn.fail to https://markdown.unicorn.fail and rebrand
  • Build each parser's settings and relevant extension settings
  • Add support for optional extensions (table/attributes)
  • Make sure everything works Not done; too big of a task right now that can be offloaded to a separate issue and implemented over time: #3135359: [markdown] Add Tests
  • Add/fix documentation Not done; too big of a task right now that can be offloaded to a separate issue and implemented over time: #3135360: [markdown] Add/fix documentation
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

gisle created an issue. See original summary.

gisle’s picture

gisle’s picture

Issue summary: View changes
gisle’s picture

Issue summary: View changes
gisle’s picture

Issue summary: View changes
gisle’s picture

Issue summary: View changes
gisle’s picture

Issue summary: View changes
gisle’s picture

Issue summary: View changes
markhalliwell’s picture

Assigned: gisle » Unassigned
Category: Task » Plan
Related issues: +#2952531: Transfer ownership of Markdown [filter] module

I 100% support this.

I've gone ahead and created #2952531: Transfer ownership of Markdown [filter] module.

Once that is done, we can move forward here.

I have actually made some recent progress in some other modules I maintain/co-maintain that may help with this issue as well:

https://www.drupal.org/project/any
https://www.drupal.org/project/backport
https://www.drupal.org/project/backport_composer
https://www.drupal.org/project/composer_manager

The idea being we could probably just create 8.x code and then support a 7.x-2.x branch using the same code.

This would allow us to not have to worry about how the league/commonmark PHP library is added to a site (i.e. how composer is utilized).

markhalliwell’s picture

Title: Merge this project and the CommonMark project » Merge in the CommonMark project
markhalliwell’s picture

Title: Merge in the CommonMark project » [PP-1] Merge in the CommonMark project
Status: Active » Postponed

Actually, going to mark this as postponed until #2952531: Transfer ownership of Markdown [filter] module is resolved.

markhalliwell’s picture

Title: [PP-1] Merge in the CommonMark project » Merge in the CommonMark project
Assigned: Unassigned » markhalliwell
Category: Plan » Task
Status: Postponed » Active

This is no longer postponed.

I will try to do this something this week(end).

markhalliwell’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev
markhalliwell’s picture

Issue summary: View changes

  • markcarver committed 92854cd on 8.x-2.x
    Initial refactoring work
    
    Issue #2952435 by markcarver: Merge in the...
markhalliwell’s picture

Status: Active » Needs work

I've committed some initial refactoring work, but it's mostly just based on D8 APIs (plugins). I haven't actually tested any of this in a browser yet.

markhalliwell’s picture

Issue summary: View changes

  • markcarver committed e8108c7 on 8.x-2.x
    Update commonmark to 0.17.1 and use VERSION constant
    
    Issue #2952435 by...

  • markcarver committed 5c66034 on 8.x-2.x
    Bump PHP minimum version to 5.6.5 due to CommonMark 0.17.1 requirements...

  • markcarver committed 088d1ca on 8.x-2.x
    Add support for other parsers
    
    Issue #2952435 by markcarver: Merge in...
  • markcarver committed 5a93383 on 8.x-2.x
    Add Twig extensions
    
    Issue #2952435 by markcarver: Merge in the...
markhalliwell’s picture

Issue summary: View changes

  • markcarver committed 6c1040b on 8.x-2.x
    Fix Twig extension service class
    
    Issue #2952435 by markcarver: Merge in...
markhalliwell’s picture

Issue summary: View changes
Issue tags: +Needs tests

Do we really need an upgrade path? This is going to be vastly different from prior implementations. Not even sure what should be upgrade exactly. Thoughts?

gisle’s picture

Do we really need an upgrade path?

What upgrade are you referring to? Drupal 7 to Drupal 8?

markhalliwell’s picture

Not specifically. The 7.x version is just going to be an exact copy of the 8.x code using #2954124: Add support for Filter APIs.

I was more specifically talking about any config/settings/variables. There were only a couple, so not entirely sure its worth it.

  • markcarver committed 6766142 on 8.x-2.x
    Get services/plugins working and support Drupal 7 via Backport module...
gisle’s picture

FileSize
8.48 KB

I am not aware any config/settings/variables that can be set by the user.

The Markdown version is exposed when you enable the filter for a text format (see screen shot from the 7.x-1.x version below), but there is no way for the user to change it.

Screenshot

So as I see no need for an upgrade path.

markhalliwell’s picture

Ah, it's just 8.x-1.x that introduced the settings/config:
http://cgit.drupalcode.org/markdown/tree/config/install/markdown.setting...

And selector:
http://cgit.drupalcode.org/markdown/tree/src/Plugin/Filter/Markdown.php#n48

Yeah... I'm tempted to say that the "upgrade path" for this should just be to tell people to reconfigure their formats/filters (since there likely isn't that many).

The actual markdown filter machine name hasn't changed so content shouldn't be affected.

markhalliwell’s picture

Issue summary: View changes
markhalliwell’s picture

Issue summary: View changes

  • markcarver committed c0ab02e on 8.x-2.x
    Fix a few things for demo site
    
    Issue #2952435 by markcarver: Merge in...

  • markcarver committed 7ded0e3 on 8.x-2.x
    Fix a few more things for demo site
    
    Issue #2952435 by markcarver: Merge...

  • markcarver committed 8a9d573 on 8.x-2.x
    Point to new demonstration site
    
    Issue #2952435 by markcarver: Merge in...
markhalliwell’s picture

Issue summary: View changes

https://markdown.unicorn.fail is now live.

P.S. It may be beneficial in the future to come up with a more semantic/dedicated domain, but for now, this works.

markhalliwell’s picture

Gone ahead and created a 8.x-2.0-alpha1 release for the work so far.

This doesn't actually expose any of the settings UI yet (which still needs work), but it at least provides working parser(s).

  • markcarver committed 987d4bb on 8.x-2.x
    Add new PECL cmark extension support
    
    Issue #2952435 by markcarver:...

  • markcarver committed b704058 on 8.x-2.x
    Update README.md
    
    Issue #2952435 by markcarver: Merge in the CommonMark...

  • markcarver committed dc1fb0a on 8.x-2.x
    Introduce a render method to abstract the parse method for benchmarking...

  • markcarver committed c897a65 on 8.x-2.x
    Add low weight to markdown filter so it is ran first (by default)....

  • markcarver committed 617a40f on 8.x-2.x
    Add MarkdownBenchmark to allow parsers to be benchmarked externally more...

  • markcarver committed 867a4bf on 8.x-2.x
    Add more interfaces and benchmark averages
    
    Issue #2952435 by markcarver...

  • markcarver committed 22de60b on 8.x-2.x
    Moar work
    
    Issue #2952435 by markcarver: Merge in the CommonMark project...

  • markcarver committed 1574904 on 8.x-2.x
    Issue #2952435 by markcarver, gisle: Merge in the CommonMark project...
markhalliwell’s picture

Weekly Meeting
Every Thur 4:00 PM CDT (UTC-05:00)
Hangout: https://tinyurl.com/yblctybm
Google calendar: https://tinyurl.com/ybnvlgen
iCal: https://tinyurl.com/y9sy9oem

Going to start this just to get it on the table. Whether or not there will be much to discuss is another thing.

gisle’s picture

Can you please give the time as UTC? (this website claims that it corresponds to 2200 UTC - https://24timezones.com/difference/cst/utc - but somehow it does not make sense).

markhalliwell’s picture

It's an offset, not actual UTC time.

I suppose it's actually 4:00 PM CDT (UTC-05:00). I forgot that we're in daylight savings time here.

https://en.m.wikipedia.org/wiki/UTC%E2%88%9206:00

Just subscribe to the calendar. That should clear up any confusion.

markhalliwell’s picture

Well. No one showed up for this week's meeting...

bradjones1’s picture

@markcarver, Do you still need help getting this to a beta or full 2.x release?

markhalliwell’s picture

Assigned: markhalliwell » Unassigned

Some help would be nice. There are only a few remaining tasks:

  • Build each parser's settings and relevant extension settings
  • Add support for optional extensions (table/attributes)
  • Make sure everything works (add tests)
  • Add/fix documentation
markhalliwell’s picture

Issue summary: View changes

  • markcarver committed 7c102e3 on 8.x-2.x
    General clean up
    
    Issue #2952435 by markcarver: Merge in the CommonMark...

  • markcarver committed 1b8d754 on 8.x-2.x
    Set settings from the definition
    
    Issue #2952435 by markcarver: Merge in...

  • markcarver committed d6e8150 on 8.x-2.x
    Work on getting extensions going
    
    Issue #2952435 by markcarver: Merge in...
mikejw’s picture

Hi Mark - I have a bit of time to help you out if you need / want.

Can see you have a bit of momentum going now but let me know if you need assistance - personally, beyond what you are doing with the GFM environment and the inbuilt extensions, I am keen to also get the footnotes extension working (https://github.com/rezozero/commonmark-ext-footnotes) - perhaps I could help getting that in there as that would be a good example of an optional extension? Or would you prefer I wait until you have got the last bit of the extension settings done?

markhalliwell’s picture

Please, feel free to create new issues as you see fit. I will welcome any and all help :D Just keep in mind that I'm still rearranging some things, so be prepared to rebase/reroll your patches.

edit: It will be easier to merge new/additive code and I will attempt to do that often prior to any more of my work.

geek-merlin’s picture

Nice this is rolling. Rock on!

mikejw’s picture

Ok, sounds good - I'll just start with making a footnotes issue. Also happy to help with documentation when you have locked things down a bit more, let me know.

Oh and I see tests are on the list and don't exist yet - I could take a crack at starting on something there.

mikejw’s picture

FileSize
1.79 KB

Small patch that fixes the version working.

markhalliwell’s picture

+++ b/src/Plugin/Markdown/Parsedown.php
@@ -40,11 +40,13 @@ class Parsedown extends BaseParser implements MarkdownParserBenchmarkInterface {
+
+    return NULL;

We don't have to explicitly return NULL here.

---

You can create new issues for specific patches. This issue is primarily a catch-all for me specifically as I don't know I'll be touching until I do (and often lumps a bunch of changes together in a single commit).

Also, at some point, we're going to need to re-test all this with https://www.drupal.org/project/backport for 7.x support.

mikejw’s picture

Ahh yep, think just needed to remove the constraint of returning a string. I'll create specific issues and patches from now on 👍

Wim Leers’s picture

#53: very nice to see this moving forward again! 🥳

#57: yay, thanks for pushing footnotes forward! Definitely missing that right now.

  • markcarver committed a6ffd3f on 8.x-2.x
    Refactor code so it’s more abstract and clear what does what
    
    Issue #...
markhalliwell’s picture

Okie dokie. I know #62 is a lot, but honestly it's really necessary. I'll try to break down what's going on and why:

  • Removed hard dependency on league/commonmark (it's now just a composer suggestion and also the lowest plugin weight so it's selected first on install if multiple parsers are detected).
  • The module will not install if there are no parsers detected (see markdown.install which has been complely overhauled). Note: there is a massive and ugly regression in Drush 9+ that doesn't actually allow this to work properly though: https://github.com/drush-ops/drush/pull/4337
  • Lowered PHP version limit to 5.5.9 (Drupal 8.0.0 minimum). This allows parsers that don't require higher PHP versions like Parsedown to be used on sites where this may be an issue.
  • The main module was doing way too much and is now split out into separate sub-modules: markdown_benchmark, markdown_filter, markdown_twig. This allows sites to only enable need certain functionality that they actually need. The main module will serve as the primary APIs and services for interacting with parsers and extensions.
  • New "global" Markdown settings form located at /admin/config/content/markdown. Previous code made this functionality really wonky and unreliable unless it was tied to a filter (that shouldn't be the case). The filter's settings will eventually extend from this form to save code.
  • New administer markdown permission added for ^.
  • New schema mapping added for proper parser and extension settings config export.
  • I went ahead and created a #markdown channel on slack.

Still todo in regards to the above (which I plan on finishing up tomorrow):

  • Finalize markdown_filter (haven't focused on it since I split it out, so not sure what is or isn't working there)
  • Cleanup the rest of the code so it isn't referencing any "filter" parameters or configuration. If the markdown_filter module is used, it should pass along the configuration to the parser. The parser doesn't need to know about the filter.
  • Specifically cleanup \Drupal\markdown\Markdown so it doesn't have long winded parameters in methods like $parser = NULL, $filter = NULL, AccountInterface $account = NULL, LanguageInterface $language = NULL.
  • Maybe some initial/rudamentary tests.
  • Likely other stuff I'm not remembering at the moment, but I'll probably get to most of it.

edit: oh and I've also been slowing working on getting https://markdown.unicorn.fail back up and running

geek-merlin’s picture

Impressive! Once it's beta enough, i can push it to our internal ticketforum for live beta.

  • markcarver committed c51d7e7 on 8.x-2.x
    Fix schema, work on markdown_filter
    
    Issue #2952435 by markcarver: Merge...

  • markcarver committed 30a7194 on 8.x-2.x
    Standardize on league/commonmark plugin name and league/commonmark-ext...

  • markcarver committed 63209e1 on 8.x-2.x
    More filter extraction, created MarkdownSettings, various bug fixes...

  • markcarver committed f6a0f6b on 8.x-2.x
    Fix config saving
    
    Issue #2952435 by markcarver: Merge in the CommonMark...

  • markcarver committed 26fdcbf on 8.x-2.x
    Finalized, more or less, the parser + extension APIs
    
    Issue #2952435 by...
markhalliwell’s picture

Phew. Ok. I believe the majority of the parser + extension APIs are relatively "stable" now.

Two new CommonMark extensions were implemented (need to flush out extension settings and dependencies):
#3131218: CommonMark Extension: Heading Permalinks
#3131220: CommonMark Extension: Table of Contents

The UI has been completely redone using vertical tabs and summaries.

Every parser now also has "Allowed HTML tags" settings which is really just a slightly customized FilterHtml implementation to allow more permissive global attributes.

Still need to do a little clean up on a few things:

We're close to a stable release! I'm thinking that once the above is done, we can release an RC and then in a month maybe a stable?

(edit) p.s. I still may rename/move some classes/namespaces around. Would like to organize it a tad better, but... naming things--

markhalliwell’s picture

Status: Needs work » Needs review

Also, I would say that we're at a good stage for people to start reviewing.

kim.pepper’s picture

What's the best way to review the changes? Just look everything on the 8.x-2.x branch?

markhalliwell’s picture

Yeah, that would probably be the best. Alternatively, you could browse the commit history to see how much I went back and forth :D

Although, it may be better to just install it from scratch and look at it via the UI and filters, etc. See if any "jumps out".

markhalliwell’s picture

Here are some screenshots for those who are interested but don't have an install ATM:

Parser:

Heading Permalink:

Table of Contents:

Wim Leers’s picture

#63: thanks for all that progress, and thanks for the thoughtful overview! Just one question: isn't the main purpose always filtering? Why have the filter plugin live in a separate markdown_filter module?

#74: wow, thanks for sharing those (pretty!) screenshots! I get the sense you like strict separation of concerns, strict adherence to best practices and strict validation. If that hunch is right, I think you'll like the approach I took in the CDN module with regards to ensuring strict validation of configuration: #2969065: Use typed config validation constraints for validation of cdn.settings simple config. It's basically where core is headed too, but which nobody has energy/time for. It's building on foundations that already exist in core for a long time now. It's got +1 from config system maintainers. I think you might want to do something similar prior to a stable release because it can give you much more confidence about how people use this module, and reduce the number of support requests/bug reports :)

markhalliwell’s picture

Just one question: isn't the main purpose always filtering?

No, or rather not anymore. It's just the most common (end-user) use case. The reality is that being able to parse markdown shouldn't be tied to (dependent on) any Drupal "filter (text_format)". It should be able to be invoked programmatically in a standalone fashion.

I'm primarily thinking of #1674976: Allow README.md to optionally render as the project page where the use of a field/filter wouldn't really be necessary as it could simply parse the project's README.md file directly (see Markdown::loadPath(), method for loading/caching markdown files based on mtime).

Another use case I know of personally is https://drupal-bootstrap.org where the project markdown files are parsed in API hooks and would be even more burdened if it had to process everything through some custom filter (text_format) which requires setting up an actual working text_format, complete with its own config, filters, roles, etc.

Why have the filter plugin live in a separate markdown_filter module?

So, I did this initially because I was attempting to separate concerns and decouple all the "filter" stuff from the main module... for reasons ^.

Now that it's sufficiently decoupled and it's maintained more or less a relatively small size, I can seem moving it back into the main module again. Perhaps also the markdown_twig sub-module as well. This was my "p.s." remark at the end of #70 about renaming/moving some classes/namespaces around.

The markdown_benchmarks module should definitely remain separate as it's only intended for specific use cases.

I get the sense you like strict separation of concerns, strict adherence to best practices and strict validation.

I've definitely been leaning more towards that direction over the years. It just makes things very clear on what they do/intend to do.

I've been able to get the config schema working the way I want it:
https://git.drupalcode.org/project/markdown/-/blob/26fdcbfdcca58961d265d...

As far as constraints and validations, that's certainly something we can add in down the road, but I don't think it's necessarily a release blocker.

If it means that implementing it will break something and cause BC issues, then we can just bump the major again.

For now, I think the config types should be sufficient given that there isn't really a whole lot of validations that need to take place currently. Most settings are boolean/select based; a few are text fields here and there.

Given that the parsers themselves typically validate their settings or, at the very least, would likely just ignore it if something was awry, I think we can get away with exceptions being thrown in the unlikely event something was completely borked.

I'd rather not delay what has already taken a long time, especially given that it appears that it's dependent on my available time.

Ambient.Impact’s picture

@markcarver Awesome work! It shows how much work you've put into this.

  • markcarver committed 2d68b11 on 8.x-2.x
    Implement render strategy + more refactoring
    
    Issue #2952435 by...
  • markcarver committed 7bb8f8a on 8.x-2.x
    Remove “guidelines” so they can be done right in a follow-up issue...

  • markcarver committed 8708dbd on 8.x-2.x
    Remove “markdown_benchmark” so it can be done right in a follow-up issue...
markhalliwell’s picture

Issue summary: View changes
Status: Needs review » Fixed
Issue tags: -Needs tests
FileSize
1.84 MB

OK.

Last "refactor", I swear lol

Sorry, the majority of this one was to make way for the introduction of Render Strategy:

So this is two-fold:

  1. Intentionally separates the XSS handling from the parsers. If a parser has settings relevant to this topic and there's a [Drupal] render strategy being used, it disables those settings so they're not in conflict. This is, primarily, to assist users who may not understand how all this is supposed to work together; in harmony.
  2. Introduces as new @MarkdownAllowedHtml plugin that can literally be used anywhere: modules, themes, filters, parsers, extensions... where ever. I was partially inspired by #3133121: Support embedded media and realized that knowing exactly what should be allowed through an XSS filter can be extremely complex. This is an attempt to simplify (as much as possible) that complexity. This also means that the default "Allowed HTML" is a much shorter list as the idea is to enable only what's needed as it's needed.

Some other noteworthy mentions:

With all this being said, I'm closing this issue as "Fixed".

If you find any issues or bugs, please just create a new issue so we can better keep track of things now.

kim.pepper’s picture

Thanks Mark!

geek-merlin’s picture

> Render Strategy

Wow! A module to rule them all.

markhalliwell’s picture

Now that #3129847: Add support for CommonMark extensions is done, I've gone ahead and created 8.x-2.0-rc1.

If y'all can test it out this week, that'd be great. Also, if anyone feels like tackling some documentation, that would be helpful too.

If no major issues creep up in the next week or so, my plan is to push a stable 8.x-2.0 release this next weekend (5/16).

Wim Leers’s picture

#76:

I'm primarily thinking of #1674976: Allow README.md to optionally render as the project page where the use of a field/filter wouldn't really be necessary

Makes sense, thanks for pointing that out! :)

Now that it's sufficiently decoupled and it's maintained more or less a relatively small size, I can seem moving it back into the main module again.

+1

The markdown_benchmarks module should definitely remain separate as it's only intended for specific use cases.

+1

As far as constraints and validations, that's certainly something we can add in down the road, but I don't think it's necessarily a release blocker.

+1

Given that the parsers themselves typically validate their settings or, at the very least, would likely just ignore it if something was awry, I think we can get away with exceptions being thrown in the unlikely event something was completely borked.

+1


#80: once again epic work!

geek-merlin’s picture

Now i found some time to update this module on a quite heavy project. Some notes on this:

* Doing drush updb with RC1 sent me into the hard to spot #3136267: Error: Unsupported operand types in SettingsForm.php on all updates that messed with user roles
* Using todays dev #df3e4119703 sent me into #3138551: Fatal error: Interface 'League\CommonMark\Extension\ExtensionInterface' not found but composer require league/commonmarkmade the update run then

More notes to come.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Farnoosh’s picture

I am having difficulty following this issue. Markdown 8.x-2.0-alpha1 installed on Drupal core 8.8.4. Now updating to Drupal 8.9.6. What steps should I take to be able to use Markdown in the Drupal 8.9.6 project? Do I have to install league/commonmark? Any help would be much appreciated.

Ambient.Impact’s picture

#87: Yes, you need to also require league/commonmark to use CommonMark with Markdown 2.x. There's actually a 8.x-2.0-rc1 release, which likely has a bunch of improvements over the alpha.

Farnoosh’s picture

Thank you, @Ambient.Impact!