diff --git a/README.md b/README.md
index 1065bda0..8da100f2 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,7 @@ is not needed to install packages with these frameworks:
| Vanilla | `vanilla-plugin`
`vanilla-theme`
| Vgmcp | `vgmcp-bundle`
`vgmcp-theme`
| WHMCS | `whmcs-addons`
`whmcs-fraud`
`whmcs-gateways`
`whmcs-notifications`
`whmcs-registrars`
`whmcs-reports`
`whmcs-security`
`whmcs-servers`
`whmcs-social`
`whmcs-support`
`whmcs-templates`
`whmcs-includes`
+| Winter CMS | **`winter-module`
`winter-plugin`
`winter-theme`**
| Wolf CMS | `wolfcms-plugin`
| WordPress | `wordpress-plugin`
`wordpress-theme`
`wordpress-muplugin`
`wordpress-dropin`
| YAWIK | `yawik-module`
diff --git a/src/Composer/Installers/Installer.php b/src/Composer/Installers/Installer.php
index cf3a09ac..82e7236b 100644
--- a/src/Composer/Installers/Installer.php
+++ b/src/Composer/Installers/Installer.php
@@ -78,7 +78,7 @@ class Installer extends LibraryInstaller
'october' => 'OctoberInstaller',
'ontowiki' => 'OntoWikiInstaller',
'oxid' => 'OxidInstaller',
- 'osclass' => 'OsclassInstaller',
+ 'osclass' => 'OsclassInstaller',
'pxcms' => 'PxcmsInstaller',
'phpbb' => 'PhpBBInstaller',
'pimcore' => 'PimcoreInstaller',
@@ -110,6 +110,7 @@ class Installer extends LibraryInstaller
'userfrosting' => 'UserFrostingInstaller',
'vanilla' => 'VanillaInstaller',
'whmcs' => 'WHMCSInstaller',
+ 'winter' => 'WinterInstaller',
'wolfcms' => 'WolfCMSInstaller',
'wordpress' => 'WordPressInstaller',
'yawik' => 'YawikInstaller',
diff --git a/src/Composer/Installers/WinterInstaller.php b/src/Composer/Installers/WinterInstaller.php
new file mode 100644
index 00000000..3dd841cb
--- /dev/null
+++ b/src/Composer/Installers/WinterInstaller.php
@@ -0,0 +1,47 @@
+ 'modules/{$name}/',
+ 'plugin' => 'plugins/{$vendor}/{$name}/',
+ 'theme' => 'themes/{$name}/'
+ );
+
+ /**
+ * Format package name.
+ *
+ * For package type winter-plugin, cut off a trailing '-plugin' if present.
+ *
+ * For package type winter-theme, cut off a trailing '-theme' if present.
+ *
+ */
+ public function inflectPackageVars($vars)
+ {
+ if ($vars['type'] === 'winter-plugin') {
+ return $this->inflectPluginVars($vars);
+ }
+
+ if ($vars['type'] === 'winter-theme') {
+ return $this->inflectThemeVars($vars);
+ }
+
+ return $vars;
+ }
+
+ protected function inflectPluginVars($vars)
+ {
+ $vars['name'] = preg_replace('/^oc-|-plugin$/', '', $vars['name']);
+ $vars['vendor'] = preg_replace('/[^a-z0-9_]/i', '', $vars['vendor']);
+
+ return $vars;
+ }
+
+ protected function inflectThemeVars($vars)
+ {
+ $vars['name'] = preg_replace('/^oc-|-theme$/', '', $vars['name']);
+
+ return $vars;
+ }
+}
diff --git a/tests/Composer/Installers/Test/WinterInstallerTest.php b/tests/Composer/Installers/Test/WinterInstallerTest.php
new file mode 100644
index 00000000..7bcb5a45
--- /dev/null
+++ b/tests/Composer/Installers/Test/WinterInstallerTest.php
@@ -0,0 +1,89 @@
+installer = new WinterInstaller(
+ new Package('NyanCat', '4.2', '4.2'),
+ new Composer()
+ );
+ }
+
+ /**
+ * @dataProvider packageNameInflectionProvider
+ */
+ public function testInflectPackageVars($type, $vendor, $name, $expectedVendor, $expectedName)
+ {
+ $this->assertEquals(
+ $this->installer->inflectPackageVars(array(
+ 'vendor' => $vendor,
+ 'name' => $name,
+ 'type' => $type
+ )),
+ array('vendor' => $expectedVendor, 'name' => $expectedName, 'type' => $type)
+ );
+ }
+
+ public function packageNameInflectionProvider()
+ {
+ return array(
+ array(
+ 'winter-plugin',
+ 'acme',
+ 'subpagelist',
+ 'acme',
+ 'subpagelist',
+ ),
+ array(
+ 'winter-plugin',
+ 'acme',
+ 'subpagelist-plugin',
+ 'acme',
+ 'subpagelist',
+ ),
+ array(
+ 'winter-plugin',
+ 'acme',
+ 'semanticwinter',
+ 'acme',
+ 'semanticwinter',
+ ),
+ // tests vendor name containing a hyphen
+ array(
+ 'winter-plugin',
+ 'foo-bar-co',
+ 'blog',
+ 'foobarco',
+ 'blog'
+ ),
+ // tests that exactly one '-theme' is cut off
+ array(
+ 'winter-theme',
+ 'acme',
+ 'some-theme-theme',
+ 'acme',
+ 'some-theme',
+ ),
+ // tests that names without '-theme' suffix stay valid
+ array(
+ 'winter-theme',
+ 'acme',
+ 'someothertheme',
+ 'acme',
+ 'someothertheme',
+ ),
+ );
+ }
+}