├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── _bootstrap └── index.php ├── _build ├── build.config.sample.php ├── build.schema.php ├── build.transport.php ├── data │ ├── events │ │ └── events.articles.php │ ├── transport.chunks.php │ ├── transport.plugins.php │ ├── transport.settings.php │ ├── transport.snippets.php │ ├── transport.templates.php │ └── transport.tvs.php ├── includes │ └── functions.php └── resolvers │ ├── dbfields.resolver.php │ ├── dependencies.resolver.php │ ├── extpack.resolver.php │ └── tvs.resolver.php ├── assets └── components │ └── articles │ ├── connector.php │ ├── css │ └── mgr.css │ ├── images │ ├── comments-icon-w.png │ ├── comments-icon.png │ └── cup.png │ ├── js │ ├── article │ │ ├── create.js │ │ └── update.js │ ├── articles.js │ ├── container │ │ ├── articles.import.window.js │ │ ├── container.articles.grid.js │ │ ├── container.common.js │ │ ├── create.js │ │ └── update.js │ └── extras │ │ ├── combo.js │ │ └── tagfield.js │ ├── themes │ └── default │ │ ├── images │ │ ├── bg.gif │ │ ├── border.gif │ │ ├── content-bg.gif │ │ ├── dots.gif │ │ ├── facebook.gif │ │ ├── feed.png │ │ ├── firefox-gray.jpg │ │ ├── gravatar.jpg │ │ ├── header-image.gif │ │ ├── header-search.gif │ │ ├── image.gif │ │ ├── left-tab.gif │ │ ├── quote.gif │ │ ├── right-tab.gif │ │ ├── rss-icon.jpg │ │ ├── search.gif │ │ ├── sh │ │ │ ├── help.png │ │ │ ├── magnifier.png │ │ │ ├── page_white_code.png │ │ │ ├── page_white_copy.png │ │ │ └── printer.png │ │ ├── thumb.jpg │ │ └── twitter.png │ │ └── style.css │ └── twitter.auth.php ├── core └── components │ └── articles │ ├── bootstrap.php │ ├── controllers │ ├── article │ │ ├── create.class.php │ │ └── update.class.php │ └── container │ │ ├── create.class.php │ │ └── update.class.php │ ├── docs │ ├── changelog.txt │ ├── license.txt │ └── readme.txt │ ├── elements │ ├── chunks │ │ ├── archivegroupbyyear.chunk.tpl │ │ ├── articlerow.chunk.tpl │ │ ├── articleslatestpost.chunk.tpl │ │ ├── articlesrss.chunk.tpl │ │ ├── articlesrsscategorynode.chunk.tpl │ │ └── articlesrssitem.chunk.tpl │ ├── plugins │ │ └── articles.plugin.php │ ├── snippets │ │ ├── snippet.articles.php │ │ └── snippet.articlesstringsplitter.php │ └── templates │ │ ├── articlescontainertemplate.tpl │ │ └── articletemplate.tpl │ ├── lexicon │ ├── cs │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── de │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── en │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── fr │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── it │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── nl │ │ ├── default.inc.php │ │ └── frontend.inc.php │ └── ru │ │ ├── default.inc.php │ │ └── frontend.inc.php │ ├── schema │ └── articles.mysql.schema.xml │ └── src │ ├── Articles.php │ ├── Model │ ├── Article.php │ ├── ArticleCreateProcessor.php │ ├── ArticleUpdateProcessor.php │ ├── ArticlesContainer.php │ ├── ArticlesContainerCreateProcessor.php │ ├── ArticlesContainerUpdateProcessor.php │ ├── ArticlesRouter.php │ ├── Import │ │ ├── ArticlesImport.php │ │ ├── ArticlesImportBlogger.php │ │ ├── ArticlesImportModx.php │ │ └── ArticlesImportWordpress.php │ ├── Notification │ │ ├── ArticlesNotification.php │ │ ├── ArticlesNotificationTwitter.php │ │ ├── OAuthLib │ │ │ └── lib.oauth.php │ │ └── TwitterOAuth.php │ ├── Update │ │ ├── ArticlesPingomatic.php │ │ └── ArticlesUpdateService.php │ ├── metadata.mysql.php │ └── mysql │ │ ├── Article.php │ │ └── ArticlesContainer.php │ └── Processors │ ├── Article │ ├── DeleteMultiple.php │ ├── GetList.php │ ├── Ping.php │ ├── PublishMultiple.php │ ├── UnDeleteMultiple.php │ └── UnPublishMultiple.php │ ├── Container │ └── Import.php │ └── Extras │ └── GetTags.php └── readme.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve Articles 4 | 5 | --- 6 | 7 | ## Bug report 8 | ### Summary 9 | Quick summary what's this issue about. 10 | 11 | ### Step to reproduce 12 | How to reproduce the issue, including custom code if needed. 13 | 14 | ### Observed behavior 15 | How it behaved after following steps above. 16 | 17 | ### Expected behavior 18 | How it should behave after following steps above. 19 | 20 | ### Environment 21 | Articles version, MODX version, apache/nginx and version, mysql version, browser, etc. Any relevant information. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea that makes Articles even better 4 | 5 | --- 6 | 7 | ## Feature request 8 | ### Summary 9 | Quick summary what's this feature request about. 10 | 11 | ### Why is it needed? 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | ### Suggested solution(s) 15 | A clear and concise description of what you want to happen. 16 | 17 | ### Related issue(s)/PR(s) 18 | Let us know if this is related to any issue/pull request. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What does it do? 2 | Describe the technical changes you did. 3 | 4 | ### Why is it needed? 5 | Describe the issue you are solving. 6 | 7 | ### Related issue(s)/PR(s) 8 | Let us know if this is related to any issue/pull request (see https://github.com/blog/1506-closing-issues-via-pull-requests) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build/build.config.php 2 | config.core.php 3 | .idea 4 | nbproject 5 | .DS_Store 6 | _packages -------------------------------------------------------------------------------- /_build/build.config.sample.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | define('MODX_BASE_PATH', '/path/to/modx/'); 23 | define('MODX_CORE_PATH', MODX_BASE_PATH . 'core/'); 24 | define('MODX_MANAGER_PATH', MODX_BASE_PATH . 'manager/'); 25 | define('MODX_CONNECTORS_PATH', MODX_BASE_PATH . 'connectors/'); 26 | define('MODX_ASSETS_PATH', MODX_BASE_PATH . 'assets/'); 27 | 28 | define('MODX_BASE_URL','/modx/'); 29 | define('MODX_CORE_URL', MODX_BASE_URL . 'core/'); 30 | define('MODX_MANAGER_URL', MODX_BASE_URL . 'manager/'); 31 | define('MODX_CONNECTORS_URL', MODX_BASE_URL . 'connectors/'); 32 | define('MODX_ASSETS_URL', MODX_BASE_URL . 'assets/'); -------------------------------------------------------------------------------- /_build/build.schema.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use MODX\Revolution\modX; 24 | 25 | /** 26 | * Build Schema script 27 | * 28 | * @package articles 29 | * @subpackage build 30 | */ 31 | $mtime = microtime(); 32 | $mtime = explode(" ", $mtime); 33 | $mtime = $mtime[1] + $mtime[0]; 34 | $tstart = $mtime; 35 | set_time_limit(0); 36 | 37 | /* define package name */ 38 | define('PKG_NAME','Articles'); 39 | define('PKG_NAME_LOWER',strtolower(PKG_NAME)); 40 | 41 | /* define sources */ 42 | $root = dirname(dirname(__FILE__)).'/'; 43 | $sources = [ 44 | 'root' => $root, 45 | 'core' => $root.'core/components/'.PKG_NAME_LOWER.'/', 46 | 'model' => $root.'core/components/'.PKG_NAME_LOWER.'/src/Model/', 47 | 'assets' => $root.'assets/components/'.PKG_NAME_LOWER.'/', 48 | ]; 49 | 50 | /* load modx and configs */ 51 | require_once dirname(__FILE__) . '/build.config.php'; 52 | require_once MODX_CORE_PATH . 'vendor/autoload.php'; 53 | 54 | $modx= new modX(); 55 | $modx->initialize('mgr'); 56 | 57 | echo '
';
58 | $modx->setLogLevel(modX::LOG_LEVEL_INFO);
59 | $modx->setLogTarget('ECHO');
60 | 
61 | $manager= $modx->getManager();
62 | $generator= $manager->getGenerator();
63 | 
64 | $generator->parseSchema(
65 |     $sources['core'] . 'schema/' . PKG_NAME_LOWER . '.mysql.schema.xml',
66 |     $sources['core'] . '/src/',
67 |     [
68 |         'compile'         => null,
69 |         'update'          => 0,
70 |         'regenerate'      => 1,
71 |         'namespacePrefix' => 'Articles\\'
72 |     ]
73 | );
74 | 
75 | $mtime= microtime();
76 | $mtime= explode(" ", $mtime);
77 | $mtime= $mtime[1] + $mtime[0];
78 | $tend= $mtime;
79 | $totalTime= ($tend - $tstart);
80 | $totalTime= sprintf("%2.4f s", $totalTime);
81 | 
82 | echo "\nExecution time: {$totalTime}\n";
83 | 
84 | exit ();


--------------------------------------------------------------------------------
/_build/build.transport.php:
--------------------------------------------------------------------------------
  1 | 
  6 |  *
  7 |  * Articles is free software; you can redistribute it and/or modify it under the
  8 |  * terms of the GNU General Public License as published by the Free Software
  9 |  * Foundation; either version 2 of the License, or (at your option) any later
 10 |  * version.
 11 |  *
 12 |  * Articles is distributed in the hope that it will be useful, but WITHOUT ANY
 13 |  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 14 |  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 15 |  *
 16 |  * You should have received a copy of the GNU General Public License along with
 17 |  * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple
 18 |  * Place, Suite 330, Boston, MA 02111-1307 USA
 19 |  *
 20 |  * @package articles
 21 |  */
 22 | 
 23 | use MODX\Revolution\modCategory;
 24 | use MODX\Revolution\modX;
 25 | use MODX\Revolution\Transport\modPackageBuilder;
 26 | use xPDO\Transport\xPDOTransport;
 27 | 
 28 | $mtime = microtime();
 29 | $mtime = explode(' ', $mtime);
 30 | $mtime = $mtime[1] + $mtime[0];
 31 | $tstart = $mtime;
 32 | set_time_limit(0);
 33 | 
 34 | /* define package */
 35 | define('PKG_NAME','Articles');
 36 | define('PKG_NAME_LOWER',strtolower(PKG_NAME));
 37 | define('PKG_VERSION','2.0.0');
 38 | define('PKG_RELEASE','pl');
 39 | 
 40 | /* define sources */
 41 | $root = dirname(dirname(__FILE__)).'/';
 42 | $sources = [
 43 |     'root' => $root,
 44 |     'build' => $root .'_build/',
 45 |     'resolvers' => $root . '_build/resolvers/',
 46 |     'data' => $root . '_build/data/',
 47 |     'events' => $root . '_build/data/events/',
 48 |     'permissions' => $root . '_build/data/permissions/',
 49 |     'properties' => $root . '_build/data/properties/',
 50 |     'source_core' => $root.'core/components/'.PKG_NAME_LOWER,
 51 |     'source_assets' => $root.'assets/components/'.PKG_NAME_LOWER,
 52 |     'plugins' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/plugins/',
 53 |     'snippets' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/snippets/',
 54 |     'chunks' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/chunks/',
 55 |     'templates' => $root.'core/components/'.PKG_NAME_LOWER.'/elements/templates/',
 56 |     'lexicon' => $root . 'core/components/'.PKG_NAME_LOWER.'/lexicon/',
 57 |     'docs' => $root.'core/components/'.PKG_NAME_LOWER.'/docs/',
 58 |     'model' => $root.'core/components/'.PKG_NAME_LOWER.'/Model/',
 59 | ];
 60 | unset($root);
 61 | 
 62 | /* override with your own defines here (see build.config.sample.php) */
 63 | require_once $sources['build'] . '/build.config.php';
 64 | require_once $sources['build'] . '/includes/functions.php';
 65 | 
 66 | require_once MODX_CORE_PATH . 'vendor/autoload.php';
 67 | 
 68 | $modx= new modX();
 69 | $modx->initialize('mgr');
 70 | echo '
'; /* used for nice formatting of log messages */
 71 | $modx->setLogLevel(modX::LOG_LEVEL_INFO);
 72 | $modx->setLogTarget('ECHO');
 73 | 
 74 | //$modx->loadClass('transport.modPackageBuilder','',false, true);
 75 | $builder = new modPackageBuilder($modx);
 76 | 
 77 | $builder->directory = dirname(dirname(__FILE__)) . '/_packages/';
 78 | 
 79 | $builder->createPackage(PKG_NAME_LOWER,PKG_VERSION,PKG_RELEASE);
 80 | $builder->registerNamespace(PKG_NAME_LOWER,false,true,'{core_path}components/'.PKG_NAME_LOWER.'/','{assets_path}components/'.PKG_NAME_LOWER.'/');
 81 | $modx->log(modX::LOG_LEVEL_INFO,'Created Transport Package and Namespace.');
 82 | 
 83 | $modx->log(modX::LOG_LEVEL_INFO,'Adding file resolvers to category...');
 84 | 
 85 | /* load system settings */
 86 | $settings = include_once $sources['data'].'transport.settings.php';
 87 | $attributes= [
 88 |     xPDOTransport::UNIQUE_KEY => 'key',
 89 |     xPDOTransport::PRESERVE_KEYS => true,
 90 |     xPDOTransport::UPDATE_OBJECT => false,
 91 | ];
 92 | if (!is_array($settings)) { $modx->log(modX::LOG_LEVEL_FATAL,'Adding settings failed.'); }
 93 | foreach ($settings as $setting) {
 94 |     $vehicle = $builder->createVehicle($setting,$attributes);
 95 |     $builder->putVehicle($vehicle);
 96 | }
 97 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($settings).' system settings.'); flush();
 98 | unset($settings,$setting,$attributes);
 99 | 
100 | /* add subpackages */
101 | //$success = include $sources['data'].'transport.subpackages.php';
102 | //if (!$success) { $modx->log(modX::LOG_LEVEL_FATAL,'Adding subpackages failed.'); }
103 | //$modx->log(modX::LOG_LEVEL_INFO,'Added in subpackages.'); flush();
104 | //unset($success);
105 | 
106 | /* add plugins */
107 | $plugins = include $sources['data'].'transport.plugins.php';
108 | if (!is_array($plugins)) { $modx->log(modX::LOG_LEVEL_FATAL,'Adding plugins failed.'); }
109 | $attributes= [
110 |     xPDOTransport::UNIQUE_KEY => 'name',
111 |     xPDOTransport::PRESERVE_KEYS => false,
112 |     xPDOTransport::UPDATE_OBJECT => true,
113 |     xPDOTransport::RELATED_OBJECTS => true,
114 |     xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [
115 |         'PluginEvents' => [
116 |             xPDOTransport::PRESERVE_KEYS => true,
117 |             xPDOTransport::UPDATE_OBJECT => false,
118 |             xPDOTransport::UNIQUE_KEY => ['pluginid','event'],
119 |         ],
120 |     ],
121 | ];
122 | foreach ($plugins as $plugin) {
123 |     $vehicle = $builder->createVehicle($plugin, $attributes);
124 |     $builder->putVehicle($vehicle);
125 | }
126 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($plugins).' plugins.'); flush();
127 | unset($plugins,$plugin,$attributes);
128 | 
129 | /* @var modCategory $category */
130 | $category= $modx->newObject(modCategory::class);
131 | $category->set('id',1);
132 | $category->set('category',PKG_NAME);
133 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in category.'); flush();
134 | 
135 | /* add chunks */
136 | $chunks = include $sources['data'].'transport.chunks.php';
137 | if (is_array($chunks)) {
138 |     $category->addMany($chunks,'Chunks');
139 | } else { $modx->log(modX::LOG_LEVEL_FATAL,'Adding chunks failed.'); }
140 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($chunks).' chunks.'); flush();
141 | unset($chunks);
142 | 
143 | /* add snippets */
144 | $snippets = include $sources['data'].'transport.snippets.php';
145 | if (is_array($snippets)) {
146 |     $category->addMany($snippets,'Snippets');
147 | } else { $modx->log(modX::LOG_LEVEL_FATAL,'Adding snippets failed.'); }
148 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($snippets).' snippets.'); flush();
149 | unset($snippets);
150 | 
151 | /* add templates */
152 | $templates = include $sources['data'].'transport.templates.php';
153 | if (is_array($templates)) {
154 |     $category->addMany($templates,'Templates');
155 | } else { $modx->log(modX::LOG_LEVEL_FATAL,'Adding Templates failed.'); }
156 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($templates).' Templates.'); flush();
157 | unset($templates);
158 | 
159 | /* add TVs */
160 | $tvs = include $sources['data'].'transport.tvs.php';
161 | if (is_array($tvs)) {
162 |     $category->addMany($tvs,'TemplateVars');
163 | } else { $modx->log(modX::LOG_LEVEL_FATAL,'Adding TVs failed.'); }
164 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in '.count($tvs).' TVs.'); flush();
165 | unset($tvs);
166 | 
167 | /* create category vehicle */
168 | $attr = [
169 |     xPDOTransport::UNIQUE_KEY => 'category',
170 |     xPDOTransport::PRESERVE_KEYS => false,
171 |     xPDOTransport::UPDATE_OBJECT => true,
172 |     xPDOTransport::RELATED_OBJECTS => true,
173 |     xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [
174 |         'Chunks' => [
175 |             xPDOTransport::PRESERVE_KEYS => false,
176 |             xPDOTransport::UPDATE_OBJECT => true,
177 |             xPDOTransport::UNIQUE_KEY => 'name',
178 |         ],
179 |         'Snippets' => [
180 |             xPDOTransport::PRESERVE_KEYS => false,
181 |             xPDOTransport::UPDATE_OBJECT => true,
182 |             xPDOTransport::UNIQUE_KEY => 'name',
183 |         ],
184 |         'Templates' => [
185 |             xPDOTransport::PRESERVE_KEYS => false,
186 |             xPDOTransport::UPDATE_OBJECT => true,
187 |             xPDOTransport::UNIQUE_KEY => 'templatename',
188 |         ],
189 |         'TemplateVars' => [
190 |             xPDOTransport::PRESERVE_KEYS => false,
191 |             xPDOTransport::UPDATE_OBJECT => true,
192 |             xPDOTransport::UNIQUE_KEY => 'name',
193 |         ],
194 |     ]
195 | ];
196 | $vehicle = $builder->createVehicle($category,$attr);
197 | $vehicle->resolve('file', [
198 |     'source' => $sources['source_core'],
199 |     'target' => "return MODX_CORE_PATH . 'components/';",
200 | ]);
201 | $vehicle->resolve('file', [
202 |     'source' => $sources['source_assets'],
203 |     'target' => "return MODX_ASSETS_PATH . 'components/';",
204 | ]);
205 | $vehicle->resolve('php', [
206 |     'source' => $sources['resolvers'] . 'dependencies.resolver.php',
207 | ]);
208 | $vehicle->resolve('php', [
209 |     'source' => $sources['resolvers'] . 'extpack.resolver.php',
210 | ]);
211 | $vehicle->resolve('php', [
212 |     'source' => $sources['resolvers'] . 'tvs.resolver.php',
213 | ]);
214 | $vehicle->resolve('php', [
215 |     'source' => $sources['resolvers'] . 'dbfields.resolver.php',
216 | ]);
217 | //$vehicle->resolve('php',array(
218 | //    'source' => $sources['resolvers'] . 'setupoptions.resolver.php',
219 | //));
220 | $modx->log(modX::LOG_LEVEL_INFO,'Packaged in resolvers.'); flush();
221 | $builder->putVehicle($vehicle);
222 | 
223 | /* now pack in the license file, readme and setup options */
224 | $builder->setPackageAttributes([
225 |     'license' => file_get_contents($sources['docs'] . 'license.txt'),
226 |     'readme' => file_get_contents($sources['docs'] . 'readme.txt'),
227 |     'changelog' => file_get_contents($sources['docs'] . 'changelog.txt'),
228 |     //'setup-options' => array(
229 |         //'source' => $sources['build'].'setup.options.php',
230 |     //),
231 | ]);
232 | $modx->log(modX::LOG_LEVEL_INFO,'Added package attributes and setup options.');
233 | 
234 | /* zip up package */
235 | $modx->log(modX::LOG_LEVEL_INFO,'Packing up transport package zip...');
236 | $builder->pack();
237 | 
238 | $mtime= microtime();
239 | $mtime= explode(" ", $mtime);
240 | $mtime= $mtime[1] + $mtime[0];
241 | $tend= $mtime;
242 | $totalTime= ($tend - $tstart);
243 | $totalTime= sprintf("%2.4f s", $totalTime);
244 | 
245 | $modx->log(modX::LOG_LEVEL_INFO,"\n
Package Built.
\nExecution time: {$totalTime}\n"); 246 | 247 | exit (); 248 | -------------------------------------------------------------------------------- /_build/data/events/events.articles.php: -------------------------------------------------------------------------------- 1 | newObject(modPluginEvent::class); 15 | $events['OnPageNotFound']->fromArray([ 16 | 'event' => 'OnPageNotFound', 17 | 'priority' => 0, 18 | 'propertyset' => 0, 19 | ],'',true,true); 20 | 21 | $events['OnManagerPageInit']= $modx->newObject(modPluginEvent::class); 22 | $events['OnManagerPageInit']->fromArray([ 23 | 'event' => 'OnManagerPageInit', 24 | 'priority' => 0, 25 | 'propertyset' => 0, 26 | ],'',true,true); 27 | 28 | $events['OnDocPublished']= $modx->newObject(modPluginEvent::class); 29 | $events['OnDocPublished']->fromArray([ 30 | 'event' => 'OnDocPublished', 31 | 'priority' => 0, 32 | 'propertyset' => 0, 33 | ],'',true,true); 34 | 35 | $events['OnDocUnPublished']= $modx->newObject(modPluginEvent::class); 36 | $events['OnDocUnPublished']->fromArray([ 37 | 'event' => 'OnDocUnPublished', 38 | 'priority' => 0, 39 | 'propertyset' => 0, 40 | ],'',true,true); 41 | 42 | return $events; -------------------------------------------------------------------------------- /_build/data/transport.chunks.php: -------------------------------------------------------------------------------- 1 | newObject(modChunk::class); 14 | $chunks[1]->fromArray([ 15 | 'id' => 1, 16 | 'name' => 'sample.ArticlesLatestPostTpl', 17 | 'description' => 'The tpl row for the latest post. Duplicate this to override it.', 18 | 'snippet' => file_get_contents($sources['chunks'].'articleslatestpost.chunk.tpl'), 19 | ]); 20 | 21 | $chunks[2]= $modx->newObject(modChunk::class); 22 | $chunks[2]->fromArray([ 23 | 'id' => 2, 24 | 'name' => 'sample.ArticleRowTpl', 25 | 'description' => 'The tpl row for each post when listed on the main Articles Container page. Duplicate this to override it.', 26 | 'snippet' => file_get_contents($sources['chunks'].'articlerow.chunk.tpl'), 27 | ]); 28 | 29 | $chunks[3]= $modx->newObject(modChunk::class); 30 | $chunks[3]->fromArray([ 31 | 'id' => 3, 32 | 'name' => 'sample.ArticlesRss', 33 | 'description' => 'The tpl for the RSS feed. Duplicate this to override it.', 34 | 'snippet' => file_get_contents($sources['chunks'].'articlesrss.chunk.tpl'), 35 | ]); 36 | 37 | $chunks[4]= $modx->newObject(modChunk::class); 38 | $chunks[4]->fromArray([ 39 | 'id' => 4, 40 | 'name' => 'sample.ArticlesRssItem', 41 | 'description' => 'The tpl row for each RSS feed item. Duplicate this to override it.', 42 | 'snippet' => file_get_contents($sources['chunks'].'articlesrssitem.chunk.tpl'), 43 | ]); 44 | 45 | $chunks[5]= $modx->newObject(modChunk::class); 46 | $chunks[5]->fromArray([ 47 | 'id' => 5, 48 | 'name' => 'sample.ArchiveGroupByYear', 49 | 'description' => 'The tpl wrapper for archives when grouped by year.', 50 | 'snippet' => file_get_contents($sources['chunks'].'archivegroupbyyear.chunk.tpl'), 51 | ]); 52 | 53 | $chunks[6]= $modx->newObject(modChunk::class); 54 | $chunks[6]->fromArray([ 55 | 'id' => 6, 56 | 'name' => 'sample.ArticlesRssCategoryNode', 57 | 'description' => 'The tpl for each RSS category node for tagging.', 58 | 'snippet' => file_get_contents($sources['chunks'].'articlesrsscategorynode.chunk.tpl'), 59 | ]); 60 | return $chunks; -------------------------------------------------------------------------------- /_build/data/transport.plugins.php: -------------------------------------------------------------------------------- 1 | newObject(modPlugin::class); 18 | $plugins[0]->set('id',1); 19 | $plugins[0]->set('name','ArticlesPlugin'); 20 | $plugins[0]->set('description','Handles FURLs for Articles.'); 21 | $plugins[0]->set('plugincode', getSnippetContent($sources['plugins'] . 'articles.plugin.php')); 22 | $plugins[0]->set('category', 0); 23 | 24 | $events = include $sources['events'].'events.articles.php'; 25 | if (is_array($events) && !empty($events)) { 26 | $plugins[0]->addMany($events); 27 | $modx->log(xPDO::LOG_LEVEL_INFO,'Packaged in '.count($events).' Plugin Events for ArticlesPlugin.'); flush(); 28 | } else { 29 | $modx->log(xPDO::LOG_LEVEL_ERROR,'Could not find plugin events for ArticlesPlugin!'); 30 | } 31 | unset($events); 32 | 33 | return $plugins; -------------------------------------------------------------------------------- /_build/data/transport.settings.php: -------------------------------------------------------------------------------- 1 | newObject(modSystemSetting::class); 12 | $settings['articles.container_ids']->fromArray([ 13 | 'key' => 'articles.container_ids', 14 | 'value' => '', 15 | 'xtype' => 'textfield', 16 | 'namespace' => 'articles', 17 | 'area' => 'furls', 18 | ],'',true,true); 19 | $settings['articles.default_container_template']= $modx->newObject(modSystemSetting::class); 20 | $settings['articles.default_container_template']->fromArray([ 21 | 'key' => 'articles.default_container_template', 22 | 'value' => 0, 23 | 'xtype' => 'modx-combo-template', 24 | 'namespace' => 'articles', 25 | 'area' => 'site', 26 | ],'',true,true); 27 | $settings['articles.default_article_template']= $modx->newObject(modSystemSetting::class); 28 | $settings['articles.default_article_template']->fromArray([ 29 | 'key' => 'articles.default_article_template', 30 | 'value' => 0, 31 | 'xtype' => 'modx-combo-template', 32 | 'namespace' => 'articles', 33 | 'area' => 'site', 34 | ],'',true,true); 35 | $settings['articles.default_article_sort_field']= $modx->newObject(modSystemSetting::class); 36 | $settings['articles.default_article_sort_field']->fromArray([ 37 | 'key' => 'articles.default_article_sort_field', 38 | 'value' => 'createdon', 39 | 'xtype' => 'textfield', 40 | 'namespace' => 'articles', 41 | 'area' => 'site', 42 | ],'',true,true); 43 | $settings['articles.article_show_longtitle']= $modx->newObject(modSystemSetting::class); 44 | $settings['articles.article_show_longtitle']->fromArray([ 45 | 'key' => 'articles.article_show_longtitle', 46 | 'value' => false, 47 | 'xtype' => 'combo-boolean', 48 | 'namespace' => 'articles', 49 | 'area' => 'site', 50 | ],'',true,true); 51 | $settings['articles.mgr_date_format']= $modx->newObject(modSystemSetting::class); 52 | $settings['articles.mgr_date_format']->fromArray([ 53 | 'key' => 'articles.mgr_date_format', 54 | 'value' => '%b %d', 55 | 'xtype' => 'textfield', 56 | 'namespace' => 'articles', 57 | 'area' => 'site', 58 | ],'',true,true); 59 | $settings['articles.mgr_time_format']= $modx->newObject(modSystemSetting::class); 60 | $settings['articles.mgr_time_format']->fromArray([ 61 | 'key' => 'articles.mgr_time_format', 62 | 'value' => '%H:%I %p', 63 | 'xtype' => 'textfield', 64 | 'namespace' => 'articles', 65 | 'area' => 'site', 66 | ],'',true,true); 67 | 68 | return $settings; -------------------------------------------------------------------------------- /_build/data/transport.snippets.php: -------------------------------------------------------------------------------- 1 | newObject(modSnippet::class); 14 | $snippets[1]->fromArray([ 15 | 'id' => 1, 16 | 'name' => 'ArticlesStringSplitter', 17 | 'description' => 'Utility snippet for Articles; splits strings by a delimiter and chunkifys the result.', 18 | 'snippet' => file_get_contents($sources['snippets'].'snippet.articlesstringsplitter.php'), 19 | ]); 20 | 21 | $snippets[2]= $modx->newObject(modSnippet::class); 22 | $snippets[2]->fromArray([ 23 | 'id' => 2, 24 | 'name' => 'Articles', 25 | 'description' => 'Displays Articles for a Container anywhere on your MODX site.', 26 | 'snippet' => file_get_contents($sources['snippets'].'snippet.articles.php'), 27 | ]); 28 | return $snippets; -------------------------------------------------------------------------------- /_build/data/transport.templates.php: -------------------------------------------------------------------------------- 1 | newObject(modTemplate::class); 14 | $templates[1]->fromArray([ 15 | 'id' => 1, 16 | 'templatename' => 'sample.ArticlesContainerTemplate', 17 | 'description' => 'The default Template for the Articles Container. Duplicate this to override it.', 18 | 'content' => file_get_contents($sources['templates'].'articlescontainertemplate.tpl'), 19 | ]); 20 | 21 | $templates[2]= $modx->newObject(modTemplate::class); 22 | $templates[2]->fromArray([ 23 | 'id' => 2, 24 | 'templatename' => 'sample.ArticleTemplate', 25 | 'description' => 'The default Template for an Article. Duplicate this to override it.', 26 | 'content' => file_get_contents($sources['templates'].'articletemplate.tpl'), 27 | ]); 28 | 29 | return $templates; -------------------------------------------------------------------------------- /_build/data/transport.tvs.php: -------------------------------------------------------------------------------- 1 | newObject(modTemplateVar::class); 14 | $tvs[1]->fromArray([ 15 | 'id' => 1, 16 | 'name' => 'articlestags', 17 | 'description' => 'The default tags TV for Articles. Do not delete!', 18 | 'caption' => 'articlestags', 19 | 'type' => 'hidden', 20 | ]); 21 | 22 | return $tvs; -------------------------------------------------------------------------------- /_build/includes/functions.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | /** 23 | * @package articles 24 | * @subpackage build 25 | */ 26 | 27 | /** 28 | * @param string $filename 29 | * @return mixed|string 30 | */ 31 | function getSnippetContent($filename) { 32 | $o = file_get_contents($filename); 33 | $o = str_replace('','',$o); 35 | $o = trim($o); 36 | return $o; 37 | } -------------------------------------------------------------------------------- /_build/resolvers/dbfields.resolver.php: -------------------------------------------------------------------------------- 1 | xpdo) { 17 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 18 | case xPDOTransport::ACTION_INSTALL: 19 | case xPDOTransport::ACTION_UPGRADE: 20 | /** @var modX $modx */ 21 | $modx =& $object->xpdo; 22 | $modelPath = $modx->getOption('articles.core_path',null,$modx->getOption('core_path').'components/articles/').'src/'; 23 | $modx->addPackage('Articles\Model', $modelPath, null, 'Articles\\'); 24 | 25 | // Look for old class names in the database and update them 26 | $oldClassKeys = ['Article', 'ArticlesContainer']; 27 | $found = $modx->getCollection(modResource::class, [ 28 | 'class_key:IN' => $oldClassKeys 29 | ]); 30 | foreach ($found as $resource) { 31 | switch ($resource->get('class_key')) { 32 | case 'Article': 33 | $resource->set('class_key', Article::class); 34 | $resource->save(); 35 | break; 36 | case 'ArticlesContainer': 37 | $resource->set('class_key', ArticlesContainer::class); 38 | $resource->save(); 39 | break; 40 | } 41 | } 42 | break; 43 | } 44 | } 45 | return true; -------------------------------------------------------------------------------- /_build/resolvers/dependencies.resolver.php: -------------------------------------------------------------------------------- 1 | xpdo; 14 | 15 | $success = true; 16 | 17 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 18 | case xPDOTransport::ACTION_INSTALL: 19 | case xPDOTransport::ACTION_UPGRADE: 20 | /** 21 | * Define required packages: name => minimum version 22 | */ 23 | $packages = [ 24 | 'archivist' => '1.2.4', 25 | 'getpage' => '1.2.4', 26 | 'getresources' => '1.7.0', 27 | 'taglister' => '1.1.7', 28 | ]; 29 | 30 | /** @var modTransportProvider|\MODX\Revolution\Transport\modTransportProvider $provider */ 31 | $provider = $modx->getObject('transport.modTransportProvider', [ 32 | 'service_url' => 'https://rest.modx.com/extras/', 33 | ]); 34 | if (!$provider) { 35 | $modx->log(modX::LOG_LEVEL_ERROR, "Could not find MODX.com provider; can't install dependencies"); 36 | } 37 | 38 | foreach ($packages as $package_name => $version) { 39 | $modx->log(modX::LOG_LEVEL_INFO, "Installing dependency {$package_name} v{$version} (or higher)..."); 40 | 41 | $installed = $modx->getIterator('transport.modTransportPackage', [ 42 | 'package_name' => $package_name, 43 | ]); 44 | /** @var modTransportPackage|\MODX\Revolution\Transport\modTransportPackage $package */ 45 | foreach ($installed as $package) { 46 | if ($package->compareVersion($version, '<=')) { 47 | $modx->log(modX::LOG_LEVEL_INFO, "- ✓ {$package->get('signature')} already installed"); 48 | continue(2); 49 | } 50 | } 51 | 52 | 53 | $latest = $provider->latest($package_name, '>=' . $version); 54 | if (count($latest) === 0) { 55 | $modx->log(modX::LOG_LEVEL_ERROR, "- Could not find {$package_name} v{$version}+ in package provider {$provider->get('name')}"); 56 | $success = false; 57 | continue; 58 | } 59 | 60 | $latest = reset($latest); 61 | $modx->log(modX::LOG_LEVEL_INFO, "- Downloading {$latest['signature']} from {$provider->get('name')}..."); 62 | $package = $provider->transfer($latest['signature']); 63 | 64 | if (!$package) { 65 | $modx->log(modX::LOG_LEVEL_ERROR, "- Download failed :("); 66 | $success = false; 67 | continue; 68 | } 69 | 70 | $modx->log(modX::LOG_LEVEL_WARN, "--- Installing {$latest['signature']} ---"); 71 | $stime = microtime(true); 72 | $installSuccess = $package->install(); 73 | $ttime = microtime(true) - $stime; 74 | 75 | if ($installSuccess) { 76 | $modx->log(modX::LOG_LEVEL_WARN,"--- Installed {$latest['signature']} in " . number_format($ttime, 2) . "s ---"); 77 | } 78 | else { 79 | $modx->log(modX::LOG_LEVEL_ERROR,"- Installation failed. Please refer to the log above for details."); 80 | $success = false; 81 | } 82 | } 83 | 84 | } 85 | 86 | return $success; -------------------------------------------------------------------------------- /_build/resolvers/extpack.resolver.php: -------------------------------------------------------------------------------- 1 | xpdo) { 14 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 15 | case xPDOTransport::ACTION_INSTALL: 16 | case xPDOTransport::ACTION_UPGRADE: 17 | case xPDOTransport::ACTION_UNINSTALL: 18 | $modx =& $object->xpdo; 19 | 20 | // This is unnecessary in MODX 3, so we'll just remove it. 21 | if ($modx instanceof modX) { 22 | $modx->removeExtensionPackage('articles'); 23 | } 24 | break; 25 | } 26 | } 27 | return true; -------------------------------------------------------------------------------- /_build/resolvers/tvs.resolver.php: -------------------------------------------------------------------------------- 1 | xpdo) { 15 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 16 | case xPDOTransport::ACTION_INSTALL: 17 | case xPDOTransport::ACTION_UPGRADE: 18 | /** @var modX $modx */ 19 | $modx =& $object->xpdo; 20 | $modelPath = $modx->getOption('articles.core_path',null,$modx->getOption('core_path').'components/articles/').'model/'; 21 | 22 | /** @var modTemplateVar $tv */ 23 | $tv = $modx->getObject(modTemplateVar::class, [ 24 | 'name' => 'articlestags', 25 | ]); 26 | if ($tv) { 27 | $templates = ['sample.ArticlesContainerTemplate','sample.ArticleTemplate']; 28 | foreach ($templates as $templateName) { 29 | /** @var modTemplate $template */ 30 | $template = $modx->getObject(modTemplate::class, ['templatename' => $templateName]); 31 | if ($template) { 32 | /** @var modTemplateVarTemplate $templateVarTemplate */ 33 | $templateVarTemplate = $modx->getObject(modTemplateVarTemplate::class, [ 34 | 'templateid' => $template->get('id'), 35 | 'tmplvarid' => $tv->get('id'), 36 | ]); 37 | if (!$templateVarTemplate) { 38 | $templateVarTemplate = $modx->newObject(modTemplateVarTemplate::class); 39 | $templateVarTemplate->set('templateid',$template->get('id')); 40 | $templateVarTemplate->set('tmplvarid',$tv->get('id')); 41 | $templateVarTemplate->save(); 42 | } 43 | } 44 | } 45 | } 46 | break; 47 | } 48 | } 49 | return true; -------------------------------------------------------------------------------- /assets/components/articles/connector.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | /** 23 | * Articles Connector 24 | * 25 | * @package articles 26 | * @var \MODX\Revolution\modX $modx 27 | * 28 | */ 29 | 30 | use Articles\Articles; 31 | 32 | require_once dirname(dirname(dirname(dirname(__FILE__)))).'/config.core.php'; 33 | require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php'; 34 | require_once MODX_CONNECTORS_PATH.'index.php'; 35 | 36 | $corePath = $modx->getOption('articles.core_path',null,$modx->getOption('core_path').'components/articles/'); 37 | $modx->articles = new Articles($modx); 38 | 39 | $modx->lexicon->load('articles:default'); 40 | 41 | /* handle request */ 42 | $path = $modx->getOption('processorsPath', $modx->articles->config,$corePath . 'src/Processors/'); 43 | $modx->request->handleRequest([ 44 | 'processors_path' => $path, 45 | 'location' => '', 46 | ]); -------------------------------------------------------------------------------- /assets/components/articles/css/mgr.css: -------------------------------------------------------------------------------- 1 | .icon-articlescontainer .x-tree-node-icon { 2 | background-image: url(../images/cup.png) !important; 3 | } 4 | 5 | .articles-grid { 6 | margin: 0 16px; 7 | } 8 | 9 | .articles-grid-date { 10 | color:#a0a0a0; 11 | font-family:arial, Helvetica, sans-serif; 12 | font-size:20px; 13 | font-weight:bold; 14 | letter-spacing:-1px; 15 | line-height:1; 16 | padding-right:10px; 17 | text-align:right; 18 | margin-top: -4px; 19 | } 20 | .articles-grid-date .articles-grid-time { 21 | display:block; 22 | font-size:10px; 23 | font-weight:normal; 24 | margin-top:5px; 25 | } 26 | 27 | .articles-grid-comments { 28 | background: url(../images/comments-icon.png) top left no-repeat !important; 29 | height: 22px; 30 | width: 24px; 31 | padding: 3px 0 0 3px; 32 | text-align: center; 33 | } 34 | .articles-grid-comments span { 35 | font-size: 11px; 36 | font-weight: bold; 37 | line-height: 1.3em; 38 | display: inline-block; 39 | color: white; 40 | text-align: center; 41 | } 42 | .articles-comments-col-header { 43 | margin-left:7px; 44 | } 45 | .main-column a { 46 | color:#485319; 47 | line-height:1; 48 | text-decoration:none; 49 | } 50 | .main-column a:hover { 51 | color:#7a8f23; 52 | text-decoration:none; 53 | } 54 | .articles-grid .x-grid3-cell-inner { 55 | color:#808080; 56 | } 57 | 58 | 59 | .articles-panel-spacer .x-panel-body { background: transparent !important; } 60 | .actions a { 61 | -moz-border-radius:3px; 62 | -webkit-border-radius:3px; 63 | -o-border-radius:3px; 64 | -ms-border-radius:3px; 65 | -khtml-border-radius:3px; 66 | border-radius:3px; 67 | background:#dcdcdc; 68 | background:-moz-linear-gradient(center bottom,#dcdcdc 0%,#fcfcfc 100%) repeat scroll 0 0 transparent; 69 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fcfcfc),color-stop(100%,#dcdcdc)); 70 | background:-webkit-linear-gradient(center bottom,#dcdcdc 0%,#fcfcfc 100%); 71 | background:-o-linear-gradient(center bottom,#dcdcdc 0%,#fcfcfc 100%); 72 | background:-ms-linear-gradient(center bottom,#dcdcdc 0%,#fcfcfc 100%); 73 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#fcfcfc,endColorstr=#dcdcdc,GradientType=0); 74 | background:linear-gradient(center bottom,#dcdcdc 0%,#fcfcfc 100%); 75 | text-shadow:0 1px 0 #ffffff; 76 | border:1px solid #cccccc; 77 | color:#888888; 78 | display:block; 79 | font-size:11px; 80 | font-weight:bold; 81 | font-family:helvetica, arial, freesans, clean, sans-serif; 82 | padding:6px 8px; 83 | text-decoration:none; 84 | cursor:pointer; 85 | } 86 | .actions a:hover { 87 | background:#e0e0e0; 88 | background:-moz-linear-gradient(center bottom,#e0e0e0 0%,#fcfcfc 100%) repeat scroll 0 0 transparent; 89 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fcfcfc),color-stop(100%,#e0e0e0)); 90 | background:-webkit-linear-gradient(center bottom,#e0e0e0 0%,#fcfcfc 100%); 91 | background:-o-linear-gradient(center bottom,#e0e0e0 0%,#fcfcfc 100%); 92 | background:-ms-linear-gradient(center bottom,#e0e0e0 0%,#fcfcfc 100%); 93 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#fcfcfc,endColorstr=#e0e0e0,GradientType=0); 94 | background:linear-gradient(center bottom,#e0e0e0 0%,#fcfcfc 100%); 95 | color:#5b7a98; 96 | } 97 | .actions a:active { 98 | -moz-box-shadow:0 0 3px #aaaaaa inset; 99 | -webkit-box-shadow:0 0 3px #aaaaaa inset; 100 | -o-box-shadow:0 0 3px #aaaaaa inset; 101 | box-shadow:0 0 3px #aaaaaa inset; 102 | background-color:#ffffff; 103 | background-image:none; 104 | } 105 | .actions a.orange { 106 | background:#febb4a; 107 | background:-moz-linear-gradient(center bottom,#febb4a 0%,#feda71 100%) repeat scroll 0 0 transparent; 108 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#feda71),color-stop(100%,#febb4a)); 109 | background:-webkit-linear-gradient(center bottom,#febb4a 0%,#feda71 100%); 110 | background:-o-linear-gradient(center bottom,#febb4a 0%,#feda71 100%); 111 | background:-ms-linear-gradient(center bottom,#febb4a 0%,#feda71 100%); 112 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#feda71,endColorstr=#febb4a,GradientType=0); 113 | background:linear-gradient(center bottom,#febb4a 0%,#feda71 100%); 114 | border-color:#f5b74e #e7a93f #dfa138; 115 | color:#996633; 116 | text-shadow:0 1px 0 #fee1a0; 117 | } 118 | .actions a.orange:hover { 119 | background:#fec95b; 120 | background:-moz-linear-gradient(center bottom,#fec95b 0%,#fee1a0 100%) repeat scroll 0 0 transparent; 121 | background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fee1a0),color-stop(100%,#fec95b)); 122 | background:-webkit-linear-gradient(center bottom,#fec95b 0%,#fee1a0 100%); 123 | background:-o-linear-gradient(center bottom,#fec95b 0%,#fee1a0 100%); 124 | background:-ms-linear-gradient(center bottom,#fec95b 0%,#fee1a0 100%); 125 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#fee1a0,endColorstr=#fec95b,GradientType=0); 126 | background:linear-gradient(center bottom,#fec95b 0%,#fee1a0 100%); 127 | } 128 | 129 | .articles-import-intro { 130 | font-size: 11px; 131 | color: #777; 132 | background-color: #fafafa !important; 133 | padding: 0 5px; 134 | } 135 | 136 | .articles-grid .article-id { 137 | font-size: 11px; 138 | font-weight: normal; 139 | display: block; 140 | margin-bottom: 3px; 141 | padding-left: 5px; 142 | float: left; 143 | color: #aaa; 144 | } 145 | .articles-grid .main-column a { 146 | float: left; 147 | } 148 | .articles-grid .article-title-column ul { 149 | clear: both; 150 | } 151 | 152 | .ext-strict .x-form-field-trigger-wrap .x-form-text, .ext-strict .x-small-editor .x-form-field-trigger-wrap .x-form-text { 153 | text-shadow:none !important; 154 | } 155 | 156 | .bxr-field-tags .bxr-field-wrapper { 157 | margin-bottom:8px; 158 | padding-right:47px; 159 | position:relative; 160 | } 161 | 162 | .bxr-field-tags .bxr-field-wrapper:before, .bxr-field-tags .bxr-field-wrapper:after { 163 | content: "\0020"; 164 | display: block; 165 | height: 0; 166 | overflow: hidden; 167 | } 168 | 169 | .bxr-field-tags .bxr-field-wrapper:after { clear: both; } 170 | 171 | .bxr-field-tags { 172 | width:auto !important; 173 | } 174 | 175 | .bxr-field-tags .x-form-field-wrap { 176 | background:none; 177 | } 178 | 179 | .bxr-field-tags input.x-form-text.x-form-field { 180 | width: 100% !important; 181 | height:100% !important; 182 | -webkit-box-sizing:border-box; 183 | -moz-box-sizing:border-box; 184 | box-sizing:border-box; 185 | font-weight:normal; 186 | } 187 | 188 | .bxr-field-tags button { 189 | margin:0 0 0 4px; 190 | padding:0 8px; 191 | height:32px; 192 | float:left; 193 | color:#53595f; 194 | font:bold 11px tahoma, verdana, helvetica, sans-serif; 195 | border-color:#D5D5D5; 196 | position:absolute; 197 | top:0; 198 | right:0; 199 | } 200 | 201 | .bxr-field-tags .x-btn:focus { 202 | border-color:#668f16; 203 | } 204 | 205 | .bxr-field-tags .x-form-field-wrap { 206 | width: 100% !important; 207 | min-width:120px; 208 | float:left; 209 | } 210 | 211 | .bxr-field-tags .inserted-tags { 212 | margin-top:3px; 213 | } 214 | 215 | .bxr-field-tags .inserted-tags li { 216 | float:left; 217 | margin:0 4px 4px 0; 218 | } 219 | 220 | .bxr-field-tags .inserted-tags li:last-of-type { 221 | margin-right:0; 222 | } -------------------------------------------------------------------------------- /assets/components/articles/images/comments-icon-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/images/comments-icon-w.png -------------------------------------------------------------------------------- /assets/components/articles/images/comments-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/images/comments-icon.png -------------------------------------------------------------------------------- /assets/components/articles/images/cup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/images/cup.png -------------------------------------------------------------------------------- /assets/components/articles/js/articles.js: -------------------------------------------------------------------------------- 1 | var Articles = function(config) { 2 | config = config || {}; 3 | Articles.superclass.constructor.call(this,config); 4 | }; 5 | Ext.extend(Articles,Ext.Component,{ 6 | page:{},window:{},grid:{},tree:{},panel:{},combo:{},config: {},view: {},extra: {} 7 | ,connector_url: '' 8 | }); 9 | Ext.reg('Articles',Articles); 10 | 11 | Articles = new Articles(); 12 | 13 | 14 | Articles.combo.PublishStatus = function(config) { 15 | config = config || {}; 16 | Ext.applyIf(config,{ 17 | store: [[1,_('published')],[0,_('unpublished')]] 18 | ,name: 'published' 19 | ,hiddenName: 'published' 20 | ,triggerAction: 'all' 21 | ,editable: false 22 | ,selectOnFocus: false 23 | ,preventRender: true 24 | ,forceSelection: true 25 | ,enableKeyEvents: true 26 | }); 27 | Articles.combo.PublishStatus.superclass.constructor.call(this,config); 28 | }; 29 | Ext.extend(Articles.combo.PublishStatus,MODx.combo.ComboBox); 30 | Ext.reg('articles-combo-publish-status',Articles.combo.PublishStatus); 31 | 32 | Articles.combo.FilterStatus = function(config) { 33 | config = config || {}; 34 | Ext.applyIf(config,{ 35 | store: [['',_('articles.all')],['published',_('published')],['unpublished',_('unpublished')],['deleted',_('deleted')]] 36 | ,name: 'filter' 37 | ,hiddenName: 'filter' 38 | ,triggerAction: 'all' 39 | ,editable: false 40 | ,selectOnFocus: false 41 | ,preventRender: true 42 | ,forceSelection: true 43 | ,enableKeyEvents: true 44 | ,emptyText: _('articles.filter_ellipsis') 45 | }); 46 | Articles.combo.FilterStatus.superclass.constructor.call(this,config); 47 | }; 48 | Ext.extend(Articles.combo.FilterStatus,MODx.combo.ComboBox); 49 | Ext.reg('articles-combo-filter-status',Articles.combo.FilterStatus); 50 | 51 | Articles.PanelSpacer = { html: '
' ,border: false, cls: 'articles-panel-spacer' }; -------------------------------------------------------------------------------- /assets/components/articles/js/container/create.js: -------------------------------------------------------------------------------- 1 | 2 | Articles.page.CreateArticlesContainer = function(config) { 3 | config = config || {record:{}}; 4 | config.record = config.record || {}; 5 | Ext.applyIf(config,{ 6 | panelXType: 'articles-panel-container' 7 | }); 8 | config.canDuplicate = false; 9 | config.canDelete = false; 10 | Articles.page.CreateArticlesContainer.superclass.constructor.call(this,config); 11 | }; 12 | Ext.extend(Articles.page.CreateArticlesContainer,MODx.page.CreateResource,{ 13 | 14 | }); 15 | Ext.reg('articles-page-articles-container-create',Articles.page.CreateArticlesContainer); 16 | 17 | 18 | 19 | Articles.panel.Container = function(config) { 20 | config = config || {}; 21 | Articles.panel.Container.superclass.constructor.call(this,config); 22 | }; 23 | Ext.extend(Articles.panel.Container,MODx.panel.Resource,{ 24 | getFields: function(config) { 25 | var it = []; 26 | it.push({ 27 | title: _('articles.container') 28 | ,id: 'modx-resource-settings' 29 | ,cls: 'modx-resource-tab' 30 | ,layout: 'form' 31 | ,labelAlign: 'top' 32 | ,labelSeparator: '' 33 | ,bodyCssClass: 'tab-panel-wrapper main-wrapper' 34 | ,autoHeight: true 35 | ,defaults: { 36 | border: false 37 | ,msgTarget: 'side' 38 | } 39 | ,items: this.getMainFields(config) 40 | }); 41 | it.push({ 42 | title: _('articles.template') 43 | ,id: 'modx-articles-template' 44 | ,cls: 'modx-resource-tab' 45 | ,layout: 'form' 46 | ,labelAlign: 'top' 47 | ,labelSeparator: '' 48 | ,bodyCssClass: 'tab-panel-wrapper main-wrapper' 49 | ,autoHeight: true 50 | ,defaults: { 51 | border: false 52 | ,msgTarget: 'side' 53 | ,width: 400 54 | } 55 | ,items: this.getTemplateSettings(config) 56 | }); 57 | it.push({ 58 | title: _('articles.advanced_settings') 59 | ,id: 'modx-articles-advanced-settings' 60 | ,cls: 'modx-resource-tab' 61 | ,labelAlign: 'top' 62 | ,labelSeparator: '' 63 | ,bodyCssClass: 'tab-panel-wrapper form-with-labels' 64 | ,autoHeight: true 65 | ,items: this.getBlogSettings(config) 66 | }); 67 | if (config.show_tvs && MODx.config.tvs_below_content != 1) { 68 | it.push(this.getTemplateVariablesPanel(config)); 69 | } 70 | if (MODx.perm.resourcegroup_resource_list == 1) { 71 | it.push(this.getAccessPermissionsTab(config)); 72 | } 73 | var its = []; 74 | its.push(this.getPageHeader(config),{ 75 | id:'modx-resource-tabs' 76 | ,xtype: 'modx-tabs' 77 | ,forceLayout: true 78 | ,deferredRender: false 79 | ,collapsible: true 80 | ,itemId: 'tabs' 81 | ,items: it 82 | }); 83 | if (MODx.config.tvs_below_content == 1) { 84 | var tvs = this.getTemplateVariablesPanel(config); 85 | tvs.style = 'margin-top: 10px;visibility: visible'; 86 | its.push(tvs); 87 | } 88 | return its; 89 | } 90 | ,getPageHeader: function(config) { 91 | config = config || {record:{}}; 92 | return { 93 | html: '

'+_('articles.container_new')+'

' 94 | ,id: 'modx-resource-header' 95 | ,cls: 'modx-page-header' 96 | ,border: false 97 | ,forceLayout: true 98 | ,anchor: '100%' 99 | }; 100 | } 101 | 102 | 103 | ,getTemplateSettings: function(config) { 104 | return [{ 105 | xtype: 'articles-tab-template-settings' 106 | ,record: config.record 107 | }]; 108 | } 109 | 110 | ,getBlogSettings: function(config) { 111 | return [{ 112 | xtype: 'articles-tab-advanced-settings' 113 | ,record: config.record 114 | }]; 115 | } 116 | 117 | 118 | ,getMainLeftFields: function(config) { 119 | config = config || {record:{}}; 120 | 121 | var aliasLength = ~~MODx.config['friendly_alias_max_length'] || 0; 122 | 123 | var createPage = MODx.action ? MODx.action['resource/create'] : 'resource/create'; 124 | return [{ 125 | xtype: 'textfield' 126 | ,fieldLabel: _('articles.container_title')+'*' 127 | ,description: MODx.expandHelp ? '' : '[[*pagetitle]]
'+_('articles.container_title_desc') 128 | ,name: 'pagetitle' 129 | ,id: 'modx-resource-pagetitle' 130 | ,maxLength: 255 131 | ,anchor: '100%' 132 | ,allowBlank: false 133 | ,enableKeyEvents: true 134 | ,listeners: { 135 | 'keyup': {scope:this,fn:function(f,e) { 136 | var titlePrefix = MODx.request.a == createPage ? _('new_document') : _('document'); 137 | var title = Ext.util.Format.stripTags(f.getValue()); 138 | Ext.getCmp('modx-resource-header').getEl().update('

'+title+'

'); 139 | }} 140 | } 141 | },{ 142 | xtype: MODx.expandHelp ? 'label' : 'hidden' 143 | ,forId: 'modx-resource-pagetitle' 144 | ,html: _('articles.container_title_desc') 145 | ,cls: 'desc-under' 146 | 147 | },{ 148 | xtype: 'textfield' 149 | ,fieldLabel: _('articles.container_alias') 150 | ,description: '[[*alias]]
'+_('articles.container_alias_desc') 151 | ,name: 'alias' 152 | ,id: 'modx-resource-alias' 153 | ,maxLength: (aliasLength > 255 || aliasLength === 0) ? 255 : aliasLength 154 | ,anchor: '100%' 155 | ,value: config.record.alias || '' 156 | },{ 157 | xtype: MODx.expandHelp ? 'label' : 'hidden' 158 | ,forId: 'modx-resource-alias' 159 | ,html: _('articles.container_alias_desc') 160 | ,cls: 'desc-under' 161 | 162 | },{ 163 | xtype: 'textarea' 164 | ,fieldLabel: _('articles.container_description') 165 | ,description: '[[*description]]
'+_('articles.container_description_desc') 166 | ,name: 'description' 167 | ,id: 'modx-resource-description' 168 | ,maxLength: 255 169 | ,anchor: '100%' 170 | ,value: config.record.description || '' 171 | },{ 172 | xtype: MODx.expandHelp ? 'label' : 'hidden' 173 | ,forId: 'modx-resource-description' 174 | ,html: _('articles.container_description_desc') 175 | ,cls: 'desc-under' 176 | 177 | },{ 178 | xtype: 'hidden' 179 | ,name: 'class_key' 180 | ,id: 'modx-resource-class-key' 181 | ,value: 'ArticlesContainer' 182 | }]; 183 | } 184 | 185 | ,getMainRightFields: function(config) { 186 | config = config || {}; 187 | return [{ 188 | xtype: 'textfield' 189 | ,fieldLabel: _('resource_menutitle') 190 | ,description: MODx.expandHelp ? '' : '[[*menutitle]]
'+_('articles.container_menutitle_desc') 191 | ,name: 'menutitle' 192 | ,id: 'modx-resource-menutitle' 193 | ,maxLength: 255 194 | ,anchor: '100%' 195 | ,value: config.record.menutitle || '' 196 | },{ 197 | xtype: MODx.expandHelp ? 'label' : 'hidden' 198 | ,forId: 'modx-resource-menutitle' 199 | ,html: _('articles.container_menutitle_desc') 200 | ,cls: 'desc-under' 201 | 202 | },{ 203 | xtype: 'textfield' 204 | ,fieldLabel: _('resource_link_attributes') 205 | ,description: MODx.expandHelp ? '' : '[[*link_attributes]]
'+_('resource_link_attributes_help') 206 | ,name: 'link_attributes' 207 | ,id: 'modx-resource-link-attributes' 208 | ,maxLength: 255 209 | ,anchor: '100%' 210 | ,value: config.record.link_attributes || '' 211 | },{ 212 | xtype: MODx.expandHelp ? 'label' : 'hidden' 213 | ,forId: 'modx-resource-link-attributes' 214 | ,html: _('resource_link_attributes_help') 215 | ,cls: 'desc-under' 216 | 217 | },{ 218 | xtype: 'xcheckbox' 219 | ,boxLabel: _('resource_hide_from_menus') 220 | ,hideLabel: true 221 | ,description: '[[*hidemenu]]
'+_('resource_hide_from_menus_help') 222 | ,name: 'hidemenu' 223 | ,id: 'modx-resource-hidemenu' 224 | ,inputValue: 1 225 | ,checked: parseInt(config.record.hidemenu) || false 226 | },{ 227 | xtype: 'xcheckbox' 228 | ,boxLabel: _('resource_folder') 229 | ,hideLabel: true 230 | ,description: '[[*isfolder]]
'+_('resource_folder_help') 231 | ,name: 'isfolder' 232 | ,id: 'modx-resource-isfolder' 233 | ,inputValue: 1 234 | ,checked: parseInt(config.record.isfolder) || true 235 | },{ 236 | xtype: 'xcheckbox' 237 | ,boxLabel: _('resource_published') 238 | ,hideLabel: true 239 | ,description: '[[*published]]
'+_('resource_published_help') 240 | ,name: 'published' 241 | ,id: 'modx-resource-published' 242 | ,inputValue: 1 243 | ,checked: parseInt(config.record.published) 244 | },{ 245 | xtype: 'xdatetime' 246 | ,fieldLabel: _('resource_publishedon') 247 | ,description: '[[*publishedon]]
'+_('resource_publishedon_help') 248 | ,name: 'publishedon' 249 | ,id: 'modx-resource-publishedon' 250 | ,allowBlank: true 251 | ,dateFormat: MODx.config.manager_date_format 252 | ,timeFormat: MODx.config.manager_time_format 253 | ,dateWidth: 120 254 | ,timeWidth: 120 255 | ,value: config.record.publishedon 256 | },{ 257 | xtype: MODx.config.publish_document ? 'xdatetime' : 'hidden' 258 | ,fieldLabel: _('resource_publishdate') 259 | ,description: '[[*pub_date]]
'+_('resource_publishdate_help') 260 | ,name: 'pub_date' 261 | ,id: 'modx-resource-pub-date' 262 | ,allowBlank: true 263 | ,dateFormat: MODx.config.manager_date_format 264 | ,timeFormat: MODx.config.manager_time_format 265 | ,dateWidth: 120 266 | ,timeWidth: 120 267 | ,value: config.record.pub_date 268 | },{ 269 | xtype: MODx.config.publish_document ? 'xdatetime' : 'hidden' 270 | ,fieldLabel: _('resource_unpublishdate') 271 | ,description: '[[*unpub_date]]
'+_('resource_unpublishdate_help') 272 | ,name: 'unpub_date' 273 | ,id: 'modx-resource-unpub-date' 274 | ,allowBlank: true 275 | ,dateFormat: MODx.config.manager_date_format 276 | ,timeFormat: MODx.config.manager_time_format 277 | ,dateWidth: 120 278 | ,timeWidth: 120 279 | ,value: config.record.unpub_date 280 | }] 281 | } 282 | 283 | 284 | }); 285 | Ext.reg('articles-panel-container',Articles.panel.Container); -------------------------------------------------------------------------------- /assets/components/articles/js/extras/combo.js: -------------------------------------------------------------------------------- 1 | Articles.combo.Tag = function(config, getStore) { 2 | config = config || {}; 3 | Ext.applyIf(config,{ 4 | name: 'fake_tags' 5 | ,hiddenName: 'fake_tags' 6 | ,displayField: 'tag' 7 | ,valueField: 'tag' 8 | ,fields: ['tag'] 9 | ,mode: 'remote' 10 | ,allowAddNewData: true 11 | ,addNewDataOnBlur : false 12 | ,itemDelimiterKey: 188 13 | ,triggerAction: 'all' 14 | ,typeAheadDelay: 50 15 | ,minChars: 1 16 | ,typeAhead: true 17 | ,editable: true 18 | ,forceSelection: false 19 | ,pageSize: 20 20 | ,url: MODx.config.connector_url 21 | ,baseParams: {action: 'Articles\\Processors\\Extras\\GetTags'} 22 | }); 23 | Ext.applyIf(config,{ 24 | store: new Ext.data.JsonStore({ 25 | url: config.url 26 | ,root: 'results' 27 | ,totalProperty: 'total' 28 | ,fields: config.fields 29 | ,errorReader: MODx.util.JSONReader 30 | ,baseParams: config.baseParams || {} 31 | ,remoteSort: config.remoteSort || false 32 | ,autoDestroy: true 33 | }) 34 | }); 35 | if (getStore === true) { 36 | config.store.load(); 37 | return config.store; 38 | } 39 | Articles.combo.Tag.superclass.constructor.call(this,config); 40 | 41 | this.on('newitem', function(bs,v,f){ 42 | v = v.split(','); 43 | Ext.each(v, function(item){ 44 | item = item.replace(/^\s+|\s+$/g, ''); 45 | var newObj = { 46 | tag: item 47 | }; 48 | bs.addNewItem(newObj); 49 | }); 50 | }); 51 | 52 | this.on('removeitem', function(combo){ 53 | combo.lastQuery = ''; 54 | MODx.fireResourceFormChange(); 55 | }); 56 | 57 | this.on('blur', function(combo){ 58 | if(combo.lastQuery){ 59 | var v = combo.lastQuery.split(','); 60 | Ext.each(v, function(item){ 61 | item = item.replace(/^\s+|\s+$/g, ''); 62 | var newObj = { 63 | tag: item 64 | }; 65 | combo.addNewItem(newObj); 66 | }); 67 | } 68 | }); 69 | 70 | this.config = config; 71 | return this; 72 | }; 73 | Ext.extend(Articles.combo.Tag,Ext.ux.form.SuperBoxSelect,{ 74 | setValue : function(value){ 75 | if(!this.rendered){ 76 | this.value = value; 77 | return; 78 | } 79 | 80 | this.removeAllItems().resetStore(); 81 | this.remoteLookup = []; 82 | 83 | if(Ext.isEmpty(value)){ 84 | return; 85 | } 86 | 87 | var values = value; 88 | if(!Ext.isArray(value)){ 89 | value = '' + value; 90 | values = value.split(this.valueDelimiter); 91 | } 92 | 93 | Ext.each(values,function(val){ 94 | val = val.replace(/^\s+|\s+$/g, ''); 95 | var record = this.findRecord(this.valueField, val); 96 | if(record){ 97 | this.addRecord(record); 98 | }else if(this.mode === 'remote'){ 99 | this.remoteLookup.push(val); 100 | } 101 | },this); 102 | 103 | if(this.mode === 'remote'){ 104 | var q = this.remoteLookup.join(this.queryValuesDelimiter); 105 | this.doQuery(q,false, true); //3rd param to specify a values query 106 | } 107 | 108 | } 109 | }); 110 | Ext.reg('articles-combo-tag',Articles.combo.Tag); -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/bg.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/border.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/border.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/content-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/content-bg.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/dots.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/dots.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/facebook.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/facebook.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/feed.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/firefox-gray.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/firefox-gray.jpg -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/gravatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/gravatar.jpg -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/header-image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/header-image.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/header-search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/header-search.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/image.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/left-tab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/left-tab.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/quote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/quote.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/right-tab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/right-tab.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/rss-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/rss-icon.jpg -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/search.gif -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/sh/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/sh/help.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/sh/magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/sh/magnifier.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/sh/page_white_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/sh/page_white_code.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/sh/page_white_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/sh/page_white_copy.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/sh/printer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/sh/printer.png -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/thumb.jpg -------------------------------------------------------------------------------- /assets/components/articles/themes/default/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/assets/components/articles/themes/default/images/twitter.png -------------------------------------------------------------------------------- /assets/components/articles/twitter.auth.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use Articles\Articles; 24 | use Articles\Model\ArticlesContainer; 25 | use Articles\Model\Notification\TwitterOAuth; 26 | 27 | require_once dirname(dirname(dirname(dirname(__FILE__)))).'/config.core.php'; 28 | require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php'; 29 | require_once MODX_CONNECTORS_PATH.'index.php'; 30 | /** 31 | * Sends the user to the Twitter authentication page to authenticate Articles to allow it to post to Twitter 32 | * 33 | * @var modX $modx 34 | * @var array $scriptProperties 35 | * 36 | * @package articles 37 | * @subpackage twitter 38 | */ 39 | $corePath = $modx->getOption('articles.core_path',null,$modx->getOption('core_path').'components/articles/'); 40 | require_once $corePath.'Model/Articles.php'; 41 | $articles = new Articles($modx); 42 | $modx->lexicon->load('articles:default'); 43 | 44 | $oAuthPath = $corePath.'Model/Notification/OAuthLib/lib.oauth.php'; 45 | require_once $oAuthPath; 46 | 47 | if (empty($_REQUEST['container'])) die('No container!'); 48 | /** @var ArticlesContainer $container */ 49 | $container = $modx->getObject(ArticlesContainer::class,$_REQUEST['container']); 50 | if (empty($container)) die('Container not found!'); 51 | 52 | $keys = $container->getTwitterKeys(); 53 | $settings = $container->getProperties('articles'); 54 | define('CONSUMER_KEY',$keys['consumer_key']); 55 | define('CONSUMER_SECRET',$keys['consumer_key_secret']); 56 | 57 | $https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : false; 58 | $selfUrl= (!$https || strtolower($https) != 'on') ? 'http://' : 'https://'; 59 | $selfUrl .= $_SERVER['HTTP_HOST']; 60 | if ($_SERVER['SERVER_PORT'] != 80) $selfUrl= str_replace(':' . $_SERVER['SERVER_PORT'], '', $selfUrl); 61 | $selfUrl .= ($_SERVER['SERVER_PORT'] == 80 || ($https !== false || strtolower($https) == 'on')) ? '' : ':' . $_SERVER['SERVER_PORT']; 62 | $selfUrl .= $_SERVER['PHP_SELF']; 63 | 64 | if (empty($_REQUEST['oauth_token']) || empty($_REQUEST['oauth_verifier'])) { 65 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET); 66 | $selfUrl .= '?container='.$container->get('id'); 67 | $requestToken = $connection->getRequestToken($selfUrl); 68 | if (empty($requestToken)) die('Could not get Request Token.'); 69 | $redirectUrl = $connection->getAuthorizeURL($requestToken); 70 | if (empty($redirectUrl)) die('Could not get redirect to auth page URL.'); 71 | $modx->sendRedirect($redirectUrl); 72 | } else { 73 | $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET,$_REQUEST['oauth_token'],$_REQUEST['oauth_verifier']); 74 | $tokenCredentials = $connection->getAccessToken($_REQUEST['oauth_verifier']); 75 | if (empty($tokenCredentials) || empty($tokenCredentials['oauth_token'])) die('Error occurred while trying to get access token.'); 76 | $settings = $container->getProperties('articles'); 77 | $settings['notifyTwitterAccessToken'] = $container->encrypt($tokenCredentials['oauth_token']); 78 | $settings['notifyTwitterAccessTokenSecret'] = $container->encrypt($tokenCredentials['oauth_token_secret']); 79 | $settings['notifyTwitterUsername'] = $tokenCredentials['screen_name']; 80 | $settings['notifyTwitterUserId'] = $tokenCredentials['user_id']; 81 | $container->setProperties($settings,'articles'); 82 | $container->save(); 83 | echo ''; 84 | die(); 85 | } 86 | 87 | return ''; -------------------------------------------------------------------------------- /core/components/articles/bootstrap.php: -------------------------------------------------------------------------------- 1 | addPackage('Articles\Model', $namespace['path'] . 'src/', null, 'Articles\\'); 10 | 11 | $modx->services->add('articles', function() use ($modx) { 12 | return new Articles\Articles($modx); 13 | }); -------------------------------------------------------------------------------- /core/components/articles/controllers/article/create.class.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use Articles\Model\ArticlesContainer; 24 | 25 | /** 26 | * @package articles 27 | */ 28 | class ArticleCreateManagerController extends ResourceCreateManagerController { 29 | 30 | public function loadCustomCssJs() { 31 | $articlesAssetsUrl = $this->modx->getOption('articles.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/articles/'); 32 | $connectorUrl = $articlesAssetsUrl.'connector.php'; 33 | $articlesJsUrl = $articlesAssetsUrl.'js/'; 34 | $mgrUrl = $this->modx->getOption('manager_url',null,MODX_MANAGER_URL); 35 | $this->addJavascript($mgrUrl.'assets/modext/util/datetime.js'); 36 | $this->addJavascript($mgrUrl.'assets/modext/widgets/element/modx.panel.tv.renders.js'); 37 | $this->addJavascript($mgrUrl.'assets/modext/widgets/resource/modx.grid.resource.security.local.js'); 38 | $this->addJavascript($mgrUrl.'assets/modext/widgets/resource/modx.panel.resource.tv.js'); 39 | $this->addJavascript($mgrUrl.'assets/modext/widgets/resource/modx.panel.resource.js'); 40 | $this->addJavascript($mgrUrl.'assets/modext/sections/resource/create.js'); 41 | $this->addJavascript($articlesJsUrl.'articles.js'); 42 | $this->addJavascript($articlesJsUrl.'extras/combo.js'); 43 | $this->addJavascript($articlesJsUrl.'extras/tagfield.js'); 44 | $this->addLastJavascript($articlesJsUrl.'article/create.js'); 45 | $this->addHtml(' 46 | '); 65 | /* load RTE */ 66 | if (!empty($this->resourceArray['richtext'])) { 67 | $this->loadRichTextEditor(); 68 | } 69 | } 70 | 71 | public function getLanguageTopics() { 72 | return ['resource','articles:default']; 73 | } 74 | 75 | 76 | public function process(array $scriptProperties = []) { 77 | $placeholders = parent::process($scriptProperties); 78 | $this->getDefaultContainerSettings(); 79 | return $placeholders; 80 | } 81 | 82 | public function getDefaultContainerSettings() { 83 | /** @var ArticlesContainer $container */ 84 | $container = $this->modx->getObject(ArticlesContainer::class, [ 85 | 'id' => $this->parent->get('id'), 86 | ]); 87 | if ($container) { 88 | $settings = $container->getProperties('articles'); 89 | $this->resourceArray['template'] = $this->modx->getOption('articleTemplate',$settings,0); 90 | $this->resourceArray['richtext'] = $this->modx->getOption('articlesRichtext',$settings,1); 91 | $this->resourceArray['published'] = $this->modx->getOption('articlesPublished',$settings,$this->modx->getOption('publish_default', null, 0)); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /core/components/articles/controllers/article/update.class.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use Articles\Model\Article; 24 | use MODX\Revolution\modTemplateVar; 25 | 26 | /** 27 | * @package articles 28 | */ 29 | class ArticleUpdateManagerController extends ResourceUpdateManagerController { 30 | /** @var Article $resource */ 31 | public $resource; 32 | /** @var boolean $commentsEnabled */ 33 | public $commentsEnabled = false; 34 | public function loadCustomCssJs() { 35 | if ($this->modx->getOption('commentsEnabled',$settings,false)) { 36 | $quipCorePath = $this->modx->getOption('quip.core_path',null,$this->modx->getOption('core_path',null,MODX_CORE_PATH).'components/quip/'); 37 | if ($this->modx->addPackage('quip',$quipCorePath.'model/')) { 38 | $this->commentsEnabled = true; 39 | } 40 | } 41 | $managerUrl = $this->context->getOption('manager_url', MODX_MANAGER_URL, $this->modx->_userConfig); 42 | $articlesAssetsUrl = $this->modx->getOption('articles.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/articles/'); 43 | $quipAssetsUrl = $this->modx->getOption('quip.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/quip/'); 44 | $connectorUrl = $articlesAssetsUrl.'connector.php'; 45 | $articlesJsUrl = $articlesAssetsUrl.'js/'; 46 | $this->addJavascript($managerUrl.'assets/modext/util/datetime.js'); 47 | $this->addJavascript($managerUrl.'assets/modext/widgets/element/modx.panel.tv.renders.js'); 48 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.grid.resource.security.local.js'); 49 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.tv.js'); 50 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.js'); 51 | $this->addJavascript($managerUrl.'assets/modext/sections/resource/update.js'); 52 | $this->addJavascript($articlesJsUrl.'articles.js'); 53 | $this->addJavascript($articlesJsUrl.'extras/combo.js'); 54 | $this->addJavascript($articlesJsUrl.'extras/tagfield.js'); 55 | 56 | if($this->commentsEnabled) { 57 | $this->addCss($quipAssetsUrl.'css/mgr.css'); 58 | $this->addJavascript($quipAssetsUrl.'js/quip.js'); 59 | $this->addJavascript($quipAssetsUrl.'js/widgets/comments.grid.js'); 60 | $this->addHtml(''); 67 | } 68 | $this->addLastJavascript($articlesJsUrl.'article/update.js'); 69 | $this->addHtml(' 70 | '); 97 | /* load RTE */ 98 | $this->loadRichTextEditor(); 99 | } 100 | public function getLanguageTopics() { 101 | return ['resource','articles:default','quip:default']; 102 | } 103 | 104 | 105 | public function process(array $scriptProperties = []) { 106 | $placeholders = parent::process($scriptProperties); 107 | $this->getTagsTV(); 108 | 109 | $settings = $this->resource->getContainerSettings(); 110 | $this->resourceArray['commentsEnabled'] = $this->commentsEnabled; 111 | //$this->resourceArray['richtext'] = $this->modx->getOption('articlesRichtext',$settings,1); 112 | 113 | return $placeholders; 114 | } 115 | 116 | public function getTagsTV() { 117 | /** @var modTemplateVar $tv */ 118 | $tv = $this->modx->getObject(modTemplateVar::class, [ 119 | 'name' => 'articlestags', 120 | ]); 121 | if ($tv) { 122 | $this->resourceArray['tags'] = $this->resource->getTVValue('articlestags'); 123 | $this->resourceArray['tagsId'] = $tv->get('id'); 124 | } 125 | return $tv; 126 | } 127 | } -------------------------------------------------------------------------------- /core/components/articles/controllers/container/create.class.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use Articles\Model\ArticlesContainer; 24 | use MODX\Revolution\modTemplate; 25 | 26 | /** 27 | * @package articles 28 | */ 29 | class ArticlesContainerCreateManagerController extends ResourceCreateManagerController { 30 | /** @var ArticlesContainer $resource */ 31 | public $resource; 32 | public function loadCustomCssJs() { 33 | $this->prepareResource(); 34 | $managerUrl = $this->context->getOption('manager_url', MODX_MANAGER_URL, $this->modx->_userConfig); 35 | $articlesAssetsUrl = $this->modx->getOption('articles.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/articles/'); 36 | $connectorUrl = $articlesAssetsUrl.'connector.php'; 37 | $articlesJsUrl = $articlesAssetsUrl.'js/'; 38 | $this->resourceArray['articles_container_settings'] = $this->resource->getContainerSettings(); 39 | $this->resourceArray['isfolder'] = true; 40 | $this->addJavascript($managerUrl.'assets/modext/util/datetime.js'); 41 | $this->addJavascript($managerUrl.'assets/modext/widgets/element/modx.panel.tv.renders.js'); 42 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.grid.resource.security.local.js'); 43 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.tv.js'); 44 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.js'); 45 | $this->addJavascript($managerUrl.'assets/modext/sections/resource/create.js'); 46 | $this->addJavascript($articlesJsUrl.'articles.js'); 47 | $this->addJavascript($articlesJsUrl.'container/container.common.js'); 48 | $this->addJavascript($articlesJsUrl.'container/container.articles.grid.js'); 49 | $this->addLastJavascript($articlesJsUrl.'container/create.js'); 50 | $this->addHtml(' 51 | '); 75 | /* load RTE */ 76 | $this->loadRichTextEditor(); 77 | } 78 | public function getLanguageTopics() { 79 | return ['resource','articles:default']; 80 | } 81 | 82 | /** 83 | * Used to set values on the resource record sent to the template for derivative classes 84 | * 85 | * @return void 86 | */ 87 | public function prepareResource() { 88 | $settings = $this->resource->getProperties('articles'); 89 | if (empty($settings)) $settings = []; 90 | 91 | $defaultContainerTemplate = $this->modx->getOption('articles.default_container_template',$settings,false); 92 | if (empty($defaultContainerTemplate)) { 93 | /** @var modTemplate $template */ 94 | $template = $this->modx->getObject(modTemplate::class, ['templatename' => 'sample.ArticlesContainerTemplate']); 95 | if ($template) { 96 | $defaultContainerTemplate = $template->get('id'); 97 | } 98 | } 99 | $this->resourceArray['template'] = $defaultContainerTemplate; 100 | 101 | $defaultArticleTemplate = $this->modx->getOption('articles.default_article_template',$settings,false); 102 | if (empty($defaultArticleTemplate)) { 103 | /** @var modTemplate $template */ 104 | $template = $this->modx->getObject(modTemplate::class, ['templatename' => 'sample.ArticleTemplate']); 105 | if ($template) { 106 | $defaultArticleTemplate = $template->get('id'); 107 | } 108 | } 109 | $this->resourceArray['setting_articleTemplate'] = $defaultArticleTemplate; 110 | 111 | foreach ($settings as $k => $v) { 112 | $this->resourceArray['setting_'.$k] = $v; 113 | } 114 | } 115 | /** 116 | * Return the pagetitle 117 | * 118 | * @return string 119 | */ 120 | public function getPageTitle() { 121 | return $this->modx->lexicon('articles.container_new'); 122 | } 123 | } -------------------------------------------------------------------------------- /core/components/articles/controllers/container/update.class.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | use Articles\Model\ArticlesContainer; 24 | 25 | /** 26 | * @package articles 27 | * @var modX $modx 28 | */ 29 | class ArticlesContainerUpdateManagerController extends ResourceUpdateManagerController { 30 | /** @var ArticlesContainer $resource */ 31 | public $resource; 32 | /** @var boolean $commentsEnabled */ 33 | public $commentsEnabled = false; 34 | public function loadCustomCssJs() { 35 | $settings = $this->resource->getContainerSettings(); 36 | if ($this->modx->getOption('commentsEnabled',$settings,false)) { 37 | $quipCorePath = $this->modx->getOption('quip.core_path',null,$this->modx->getOption('core_path',null,MODX_CORE_PATH).'components/quip/'); 38 | if ($this->modx->addPackage('quip',$quipCorePath.'model/')) { 39 | $this->commentsEnabled = true; 40 | } 41 | } 42 | $managerUrl = $this->context->getOption('manager_url', MODX_MANAGER_URL, $this->modx->_userConfig); 43 | $articlesAssetsUrl = $this->modx->getOption('articles.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/articles/'); 44 | $quipAssetsUrl = $this->modx->getOption('quip.assets_url',null,$this->modx->getOption('assets_url',null,MODX_ASSETS_URL).'components/quip/'); 45 | $connectorUrl = $articlesAssetsUrl.'connector.php'; 46 | $articlesJsUrl = $articlesAssetsUrl.'js/'; 47 | $this->addJavascript($managerUrl.'assets/modext/util/datetime.js'); 48 | $this->addJavascript($managerUrl.'assets/modext/widgets/element/modx.panel.tv.renders.js'); 49 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.grid.resource.security.local.js'); 50 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.tv.js'); 51 | $this->addJavascript($managerUrl.'assets/modext/widgets/resource/modx.panel.resource.js'); 52 | $this->addJavascript($managerUrl.'assets/modext/sections/resource/update.js'); 53 | $this->addJavascript($articlesJsUrl.'articles.js'); 54 | $this->addJavascript($articlesJsUrl.'container/container.common.js'); 55 | $this->addJavascript($articlesJsUrl.'container/container.articles.grid.js'); 56 | $this->addJavascript($articlesJsUrl.'container/articles.import.window.js'); 57 | $this->addLastJavascript($articlesJsUrl.'container/update.js'); 58 | 59 | if($this->commentsEnabled) { 60 | $this->addCss($quipAssetsUrl.'css/mgr.css'); 61 | $this->addJavascript($quipAssetsUrl.'js/quip.js'); 62 | $this->addJavascript($quipAssetsUrl.'js/widgets/comments.grid.js'); 63 | $this->addHtml(''); 70 | } 71 | 72 | $this->resourceArray['articles_container_settings'] = $settings; 73 | 74 | $this->addHtml(' 75 | '); 103 | /* load RTE */ 104 | $this->loadRichTextEditor(); 105 | } 106 | public function getLanguageTopics() { 107 | return ['resource','articles:default','quip:default']; 108 | } 109 | 110 | /** 111 | * Used to set values on the resource record sent to the template for derivative classes 112 | * 113 | * @return void 114 | */ 115 | public function prepareResource() { 116 | $settings = $this->resource->getProperties('articles'); 117 | if (is_array($settings) && !empty($settings)) { 118 | foreach ($settings as $k => $v) { 119 | $this->resourceArray['setting_'.$k] = $v; 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /core/components/articles/docs/readme.txt: -------------------------------------------------------------------------------- 1 | ----------------------- 2 | Articles - 2.0.0-alpha1 3 | ----------------------- 4 | 5 | Now compatible with MODX 3! 6 | 7 | At the time of writing, MODX 3.0.0-pl has just been released and there has been a lot of refactoring work done to make Articles compatible. However, due to incompatible class keys and the way custom resources classes function (Articles makes use of these) in MODX, the developers have had to split Articles into two versions. 8 | 9 | Before upgrading to MODX 3 with Articles installed, you'll need to update Articles to at least version 1.8.0-pl. This will ensure an older incompatible version of Articles doesn't cause any errors during the upgrade process. 10 | 11 | Once upgraded to MODX 3.0, articles won't yet be fully usable. Go to the package manager, and you'll see a new version of Articles (2.0.0-alpha1 at time of writing) will be available. Download and install this to start using Articles with MODX 3! 12 | 13 | Please bear in mind that it's currently an Alpha version, and be sure to report any issues you encounter on Github: https://github.com/modxcms/Articles 14 | 15 | 16 | Original Version 17 | ----------------------- 18 | First Released: November 29th, 2011 19 | Author: Shaun McCormick 20 | License: GNU GPLv2 (or later at your option) 21 | 22 | This component is a Custom Resource Class for MODX 2.2+. It allows you to easily create Article containers (such as 23 | a Blog, News section, or Events section) on your site, complete with a custom UI for managing them and Articles. 24 | It automatically handles URL archiving, RSS feeds, comments, tags and more. 25 | 26 | Please read the official documentation at: 27 | https://docs.modx.org/current/en/extras/articles 28 | 29 | Thanks for using Articles! 30 | Shaun McCormick 31 | shaun+articles@modx.com -------------------------------------------------------------------------------- /core/components/articles/elements/chunks/archivegroupbyyear.chunk.tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/components/articles/elements/chunks/articlerow.chunk.tpl: -------------------------------------------------------------------------------- 1 |
2 |

[[+pagetitle]]

3 | 4 |
5 |

[[+introtext:default=`[[+content:ellipsis=`400`]]`]]

6 |
7 | 14 |
-------------------------------------------------------------------------------- /core/components/articles/elements/chunks/articleslatestpost.chunk.tpl: -------------------------------------------------------------------------------- 1 |
  • 2 | [[+pagetitle]] 3 | [[+publishedon:notempty=`
    - [[+publishedon:strtotime:date=`%b %d, %Y`]]`]] 4 |
  • -------------------------------------------------------------------------------- /core/components/articles/elements/chunks/articlesrss.chunk.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [[++site_name]] 5 | [[~[[*id]]?scheme=`full`]] 6 | [[*description:cdata]] 7 | en 8 | Copyright [[+year]] by [[++site_admin]]. All Rights Reserved. 9 | 120 10 | [[+content]] 11 | 12 | -------------------------------------------------------------------------------- /core/components/articles/elements/chunks/articlesrsscategorynode.chunk.tpl: -------------------------------------------------------------------------------- 1 | [[+item]] -------------------------------------------------------------------------------- /core/components/articles/elements/chunks/articlesrssitem.chunk.tpl: -------------------------------------------------------------------------------- 1 | 2 | [[+pagetitle]] 3 | [[~[[+id]]?scheme=`full`]] 4 | [[+introtext:default=`[[+content:ellipsis=`400`]]`:cdata]] 5 | [[+publishedon:strtotime:date=`%a, %d %b %Y %H:%M:%S %z`]] 6 | [[~[[+id]]?scheme=`full`]] 7 | [[+createdby:userinfo=`email`]] ([[+createdby:userinfo=`fullname`]]) 8 | [[!ArticlesStringSplitter? &string=`[[+tv.articlestags]]` &tpl=`sample.ArticlesRssCategoryNode`]] 9 | 10 | -------------------------------------------------------------------------------- /core/components/articles/elements/plugins/articles.plugin.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Articles is free software; you can redistribute it and/or modify it under the 9 | * terms of the GNU General Public License as published by the Free Software 10 | * Foundation; either version 2 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 14 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 15 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 19 | * Place, Suite 330, Boston, MA 02111-1307 USA 20 | * 21 | * @var \MODX\Revolution\modX $modx 22 | * @var array $scriptProperties 23 | * @package articles 24 | */ 25 | 26 | use Articles\Model\Article; 27 | use Articles\Model\ArticlesRouter; 28 | 29 | if (!$modx->services->has('articles')) { 30 | return; 31 | } 32 | 33 | $articles = $modx->services->get('articles'); 34 | if (!($articles instanceof Articles\Articles)) return ''; 35 | 36 | switch ($modx->event->name) { 37 | case 'OnManagerPageInit': 38 | $cssFile = $modx->getOption('articles.assets_url',null,$modx->getOption('assets_url').'components/articles/').'css/mgr.css'; 39 | $modx->regClientCSS($cssFile); 40 | break; 41 | 42 | case 'OnPageNotFound': 43 | $router = new ArticlesRouter($modx); 44 | $router->route(); 45 | return; 46 | 47 | case 'OnDocPublished': 48 | /** @var Article $resource */ 49 | $resource =& $scriptProperties['resource']; 50 | if ($resource instanceof Article) { 51 | $resource->setArchiveUri(); 52 | $resource->save(); 53 | $modx->cacheManager->refresh([ 54 | 'db' => [], 55 | 'auto_publish' => ['contexts' => [$resource->get('context_key')]], 56 | 'context_settings' => ['contexts' => [$resource->get('context_key')]], 57 | 'resource' => ['contexts' => [$resource->get('context_key')]], 58 | ]); 59 | $resource->notifyUpdateServices(); 60 | $resource->sendNotifications(); 61 | } 62 | break; 63 | case 'OnDocUnPublished': 64 | $resource =& $scriptProperties['resource']; 65 | break; 66 | 67 | } 68 | return; -------------------------------------------------------------------------------- /core/components/articles/elements/snippets/snippet.articles.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | /** 23 | * Displays a list of posts for a container 24 | * 25 | * @var modX $modx 26 | * @var array $scriptProperties 27 | * 28 | * @package articles 29 | */ 30 | 31 | use Articles\Model\ArticlesContainer; 32 | 33 | $modx->lexicon->load('articles:frontend'); 34 | 35 | $container = $modx->getOption('container', $scriptProperties,0); 36 | if (empty($container)) return ''; 37 | 38 | /** @var ArticlesContainer $container */ 39 | $container = $modx->getObject(ArticlesContainer::class, $container); 40 | if (empty($container)) return ''; 41 | 42 | $placeholderPrefix = $modx->getOption('placeholderPrefix', $scriptProperties,''); 43 | 44 | $container->getPostListingCall($placeholderPrefix); 45 | $container->getArchivistCall($placeholderPrefix); 46 | $container->getTagListerCall($placeholderPrefix); 47 | $container->getLatestPostsCall($placeholderPrefix); 48 | $settings = $container->getContainerSettings(); 49 | if ($modx->getOption('commentsEnabled',$settings,true)) { 50 | $container->getLatestCommentsCall($placeholderPrefix); 51 | $modx->setPlaceholder($placeholderPrefix.'comments_enabled',1); 52 | } else { 53 | $modx->setPlaceholder($placeholderPrefix.'comments_enabled',0); 54 | } 55 | return ''; -------------------------------------------------------------------------------- /core/components/articles/elements/snippets/snippet.articlesstringsplitter.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | /** 23 | * @var modX $modx 24 | * @var array $scriptProperties 25 | */ 26 | $string = $modx->getOption('string',$scriptProperties,''); 27 | $delimiter = $modx->getOption('delimiter',$scriptProperties,','); 28 | $tpl = $modx->getOption('tpl',$scriptProperties,'articlerssitem'); 29 | $outputSeparator = $modx->getOption('outputSeparator',$scriptProperties,"\n"); 30 | $outputSeparator = str_replace('\\n',"\n",$outputSeparator); 31 | $toPlaceholder = $modx->getOption('toPlaceholder',$scriptProperties,''); 32 | 33 | $items = explode($delimiter,$string); 34 | $items = array_unique($items); 35 | $list = []; 36 | foreach ($items as $item) { 37 | $list[] = $modx->getChunk($tpl, [ 38 | 'item' => $item, 39 | ]); 40 | } 41 | 42 | $output = implode($outputSeparator,$list); 43 | if (!empty($toPlaceholder)) { 44 | $modx->setPlaceholder($toPlaceholder,$output); 45 | return ''; 46 | } 47 | return $output; -------------------------------------------------------------------------------- /core/components/articles/elements/templates/articlescontainertemplate.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Articles - [[*pagetitle]] 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 34 |
    35 | 36 |
    37 | 38 | 39 |
    40 | [[*content]] 41 |
    42 | 43 | 44 | 45 | 46 |
    47 |
    48 | 49 |
    50 |

    Latest Posts

    51 |
      52 | [[+latest_posts]] 53 |
    54 |
    55 | 56 | [[!+comments_enabled:is=`1`:then=` 57 |
    58 |

    Latest Comments

    59 |
      60 | [[!+latest_comments]] 61 |
    62 |
    63 | `]] 64 | 65 | 66 |
    67 | 68 |
    69 | 70 | 71 |
    72 | 73 | 74 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /core/components/articles/elements/templates/articletemplate.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Articles - [[*pagetitle]] 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 33 |
    34 | 35 |
    36 | 37 | 38 |
    39 |

    [[*pagetitle]]

    40 | 47 |
    48 |

    [[*introtext]]

    49 |
    50 | [[*content]] 51 |
    52 | 53 |
    54 | 55 |
    56 | [[!+comments]] 57 |
    58 |

    Add a Comment

    59 | [[!+comments_form]] 60 |
    61 |
    62 | 63 |
    64 |
    65 | 66 |
    67 |

    Latest Posts

    68 |
      69 | [[!+latest_posts]] 70 |
    71 |
    72 | 73 | [[!+comments_enabled:is=`1`:then=` 74 |
    75 |

    Latest Comments

    76 |
      77 | [[!+latest_comments]] 78 |
    79 |
    80 | `]] 81 |
    82 | 83 |
    84 | 85 | 86 | 87 |
    88 | 89 | 90 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /core/components/articles/lexicon/cs/frontend.inc.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Articles/b3e481169320ed5e647d817cf91a1c71994cde9d/core/components/articles/lexicon/cs/frontend.inc.php -------------------------------------------------------------------------------- /core/components/articles/lexicon/de/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | * @subpackage lexicon 22 | * 23 | * @language de 24 | * Articles translated to German by Jan-Christoph Ihrens (enigmatic_user, enigma@lunamail.de) 25 | */ 26 | $_lang['articles.comments'] = 'Kommentare'; 27 | $_lang['articles.posted_by'] = 'Gepostet von'; 28 | $_lang['articles.read_more'] = 'Lesen Sie mehr'; 29 | $_lang['articles.tags'] = 'Tags'; -------------------------------------------------------------------------------- /core/components/articles/lexicon/en/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | $_lang['articles.comments'] = 'Comments'; 23 | $_lang['articles.posted_by'] = 'Posted by'; 24 | $_lang['articles.read_more'] = 'Read more'; 25 | $_lang['articles.tags'] = 'Tags'; -------------------------------------------------------------------------------- /core/components/articles/lexicon/fr/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | $_lang['articles.comments'] = 'Commentaires'; 23 | $_lang['articles.posted_by'] = 'Posté par'; 24 | $_lang['articles.read_more'] = 'Lire la suite'; 25 | $_lang['articles.tags'] = 'Tags'; -------------------------------------------------------------------------------- /core/components/articles/lexicon/it/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | * @subpackage lexicon 22 | * 23 | * @language it 24 | * Articles translated to Italian by tillilab 25 | */ 26 | $_lang['articles.comments'] = 'Commenti'; 27 | $_lang['articles.posted_by'] = 'Autore:'; 28 | $_lang['articles.read_more'] = 'Leggi tutto'; 29 | $_lang['articles.tags'] = 'Tags'; -------------------------------------------------------------------------------- /core/components/articles/lexicon/nl/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | $_lang['articles.comments'] = 'Reacties'; 23 | $_lang['articles.posted_by'] = 'Gepost door'; 24 | $_lang['articles.read_more'] = 'Meer'; 25 | $_lang['articles.tags'] = 'Tags (komma-gescheiden)'; -------------------------------------------------------------------------------- /core/components/articles/lexicon/ru/frontend.inc.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | * 22 | * @language ru 23 | * Articles translated to Russian by Ivan Klimchuk (Alroniks, ivan@klimchuk.com) 24 | */ 25 | $_lang['articles.comments'] = 'Комментарии'; 26 | $_lang['articles.posted_by'] = 'Опубликовано'; 27 | $_lang['articles.read_more'] = 'Читать дальше'; 28 | $_lang['articles.tags'] = 'Теги'; -------------------------------------------------------------------------------- /core/components/articles/schema/articles.mysql.schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /core/components/articles/src/Articles.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | namespace Articles; 24 | 25 | use MODX\Revolution\modChunk; 26 | use MODX\Revolution\modX; 27 | 28 | /** 29 | * The base service class for Articles. 30 | * 31 | * @package articles 32 | */ 33 | class Articles { 34 | /** 35 | * A reference to the modX instance 36 | * @var modX $modx 37 | */ 38 | public $modx; 39 | /** 40 | * An array of configuration properties 41 | * @var array $config 42 | */ 43 | public $config; 44 | /** 45 | * An array of cached chunks used for faster processing 46 | * 47 | * @var array $chunks 48 | */ 49 | public $chunks; 50 | 51 | /** 52 | * Create an instance of Articles. 53 | * 54 | * @param modX $modx A reference to the modX object 55 | * @param array $config A configuration array 56 | */ 57 | function __construct(modX &$modx,array $config = []) { 58 | $this->modx =& $modx; 59 | 60 | $corePath = $this->modx->getOption('articles.core_path',$config,$this->modx->getOption('core_path').'components/articles/'); 61 | $assetsUrl = $this->modx->getOption('articles.assets_url',$config,$this->modx->getOption('assets_url').'components/articles/'); 62 | $connectorUrl = $assetsUrl.'connector.php'; 63 | 64 | $this->config = array_merge([ 65 | 'assetsUrl' => $assetsUrl, 66 | 'cssUrl' => $assetsUrl.'css/', 67 | 'jsUrl' => $assetsUrl.'js/', 68 | 'imagesUrl' => $assetsUrl.'images/', 69 | 70 | 'connectorUrl' => $connectorUrl, 71 | 72 | 'corePath' => $corePath, 73 | 'srcPath' => $corePath.'src/', 74 | 'modelPath' => $corePath.'src/Model/', 75 | 'elementsPath' => $corePath.'elements/', 76 | 'snippetsPath' => $corePath.'elements/snippets/', 77 | 'tvsPath' => $corePath.'elements/tvs/', 78 | 'chunksPath' => $corePath.'elements/chunks/', 79 | 'chunkSuffix' => '.chunk.tpl', 80 | 'processorsPath' => $corePath.'processors/', 81 | ],$config); 82 | $this->modx->lexicon->load('articles:default'); 83 | } 84 | 85 | /** 86 | * Gets a Chunk and caches it; also falls back to file-based templates 87 | * for easier debugging. 88 | * 89 | * @access public 90 | * @param string $name The name of the Chunk 91 | * @param array $properties The properties for the Chunk 92 | * @return string The processed content of the Chunk 93 | */ 94 | public function getChunk($name, array $properties = []) 95 | { 96 | $chunk = null; 97 | if (!isset($this->chunks[$name])) { 98 | $chunk = $this->modx->getObject(modChunk::class, ['name' => $name],true); 99 | if (empty($chunk)) { 100 | $chunk = $this->_getTplChunk($name,$this->config['chunkSuffix']); 101 | if ($chunk == false) return false; 102 | } 103 | $this->chunks[$name] = $chunk->getContent(); 104 | } else { 105 | $o = $this->chunks[$name]; 106 | $chunk = $this->modx->newObject(modChunk::class); 107 | $chunk->setContent($o); 108 | } 109 | $chunk->setCacheable(false); 110 | return $chunk->process($properties); 111 | } 112 | 113 | /** 114 | * Returns a modChunk object from a template file. 115 | * 116 | * @access private 117 | * @param string $name The name of the Chunk. Will parse to name.chunk.tpl by default. 118 | * @param string $suffix The suffix to add to the chunk filename. 119 | * @return modChunk/boolean Returns the modChunk object if found, otherwise 120 | * false. 121 | */ 122 | private function _getTplChunk($name, $suffix = '.chunk.tpl') 123 | { 124 | $chunk = false; 125 | $f = $this->config['chunksPath'].strtolower($name).$suffix; 126 | if (file_exists($f)) { 127 | $o = file_get_contents($f); 128 | /** @var modChunk $chunk */ 129 | $chunk = $this->modx->newObject(modChunk::class); 130 | $chunk->set('name',$name); 131 | $chunk->setContent($o); 132 | } 133 | return $chunk; 134 | } 135 | 136 | public static function arrayUnique($arr) 137 | { 138 | return array_keys(array_flip($arr)); 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/ArticleCreateProcessor.php: -------------------------------------------------------------------------------- 1 | setProperty('searchable',true); 31 | $this->setProperty('richtext',true); 32 | $this->setProperty('isfolder',false); 33 | $this->setProperty('cacheable',true); 34 | $this->setProperty('clearCache',true); 35 | $this->setProperty('class_key',Article::class); 36 | return parent::beforeSet(); 37 | } 38 | 39 | /** 40 | * Override modResourceCreateProcessor::beforeSave to provide archiving 41 | * 42 | * {@inheritDoc} 43 | * @return boolean 44 | */ 45 | public function beforeSave() 46 | { 47 | $beforeSave = parent::beforeSave(); 48 | 49 | if (!$this->parentResource) { 50 | $this->parentResource = $this->object->getOne('Parent'); 51 | } 52 | 53 | if ($this->object->get('published') || $this->object->get('pub_date')) { 54 | if (!$this->setArchiveUri()) { 55 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Failed to set URI for new Article.'); 56 | } 57 | } 58 | 59 | /** @var ArticlesContainer $container */ 60 | $container = $this->modx->getObject(ArticlesContainer::class,$this->object->get('parent')); 61 | if ($container) { 62 | $settings = $container->getProperties('articles'); 63 | $this->object->setProperties($settings,'articles'); 64 | $this->object->set('richtext',!isset($settings['articlesRichtext']) || !empty($settings['articlesRichtext'])); 65 | } 66 | 67 | $this->isPublishing = $this->object->isDirty('published') && $this->object->get('published'); 68 | return $beforeSave; 69 | } 70 | 71 | /** 72 | * Set the friendly URL archive by forcing it into the URI. 73 | * @return bool|string 74 | */ 75 | public function setArchiveUri() 76 | { 77 | if (!$this->parentResource) { 78 | return false; 79 | } 80 | return $this->object->setArchiveUri(); 81 | } 82 | 83 | public function afterSave() { 84 | $afterSave = parent::afterSave(); 85 | $this->saveTemplateVariables(); 86 | if($this->object->get('clearCache')) $this->clearContainerCache(); 87 | if ($this->isPublishing) { 88 | $this->object->notifyUpdateServices(); 89 | $this->object->sendNotifications(); 90 | } 91 | return $afterSave; 92 | } 93 | 94 | /** 95 | * Clears the container cache to ensure that the container listing is updated 96 | * @return void 97 | */ 98 | public function clearContainerCache() 99 | { 100 | $this->modx->cacheManager->refresh([ 101 | 'db' => [], 102 | 'auto_publish' => ['contexts' => [$this->object->get('context_key')]], 103 | 'context_settings' => ['contexts' => [$this->object->get('context_key')]], 104 | 'resource' => ['contexts' => [$this->object->get('context_key')]], 105 | ]); 106 | } 107 | 108 | /** 109 | * Extend the saveTemplateVariables method and provide handling for the 'tags' type to store in a hidden TV 110 | * @return array|mixed 111 | */ 112 | public function saveTemplateVariables() 113 | { 114 | $tags = $this->getProperty('tags',null); 115 | if ($tags !== null) { 116 | /** @var modTemplateVar $tv */ 117 | $tv = $this->modx->getObject(modTemplateVar::class, [ 118 | 'name' => 'articlestags', 119 | ]); 120 | if ($tv) { 121 | $defaultValue = $tv->processBindings($tv->get('default_text'),$this->object->get('id')); 122 | if (strcmp($tags,$defaultValue) != 0) { 123 | /* update the existing record */ 124 | $tvc = $this->modx->getObject(modTemplateVarResource::class, [ 125 | 'tmplvarid' => $tv->get('id'), 126 | 'contentid' => $this->object->get('id'), 127 | ]); 128 | if ($tvc == null) { 129 | /** @var modTemplateVarResource $tvc add a new record */ 130 | $tvc = $this->modx->newObject(modTemplateVarResource::class); 131 | $tvc->set('tmplvarid',$tv->get('id')); 132 | $tvc->set('contentid',$this->object->get('id')); 133 | } 134 | $tvc->set('value',$tags); 135 | $tvc->save(); 136 | 137 | /* if equal to default value, erase TVR record */ 138 | } else { 139 | $tvc = $this->modx->getObject(modTemplateVarResource::class, [ 140 | 'tmplvarid' => $tv->get('id'), 141 | 'contentid' => $this->object->get('id'), 142 | ]); 143 | if (!empty($tvc)) { 144 | $tvc->remove(); 145 | } 146 | } 147 | } 148 | } 149 | return true; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /core/components/articles/src/Model/ArticleUpdateProcessor.php: -------------------------------------------------------------------------------- 1 | getProperties(); 33 | $properties['class_key'] = Article::class; 34 | $this->setProperties($properties); 35 | return $initialize; 36 | } 37 | 38 | public function beforeSet() { 39 | $this->setProperty('clearCache',true); 40 | return parent::beforeSet(); 41 | } 42 | 43 | /** 44 | * Override modResourceUpdateProcessor::beforeSave to provide archiving 45 | * 46 | * {@inheritDoc} 47 | * @return boolean 48 | */ 49 | public function beforeSave() { 50 | $afterSave = parent::beforeSave(); 51 | $container = $this->modx->getObject(ArticlesContainer::class,$this->object->get('parent')); 52 | 53 | if ($this->object->get('published') && ($this->object->isDirty('alias') || $this->object->isDirty('published'))) { 54 | if (!$this->setArchiveUri()) { 55 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Failed to set date URI.'); 56 | } 57 | } else if (($this->object->get('pub_date') && $this->object->isDirty('pub_date')) || $this->object->isDirty('pub_date')) { 58 | if (!$this->setArchiveUri()) { 59 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Failed to set date URI pub_date.'); 60 | } 61 | } else if(!$this->object->get('published') && !$this->object->get('pub_date')) { // we need to always do this because the url may have been set previously by pub_date 62 | /*$containerUri = $container->get('uri'); 63 | if (empty($containerUri)) { 64 | $containerUri = $container->get('alias'); 65 | }*/ 66 | $uri = rtrim($this->object->get('alias')); 67 | $this->object->set('uri',$uri); 68 | $this->object->set('uri_override',true); 69 | } 70 | 71 | /** @var ArticlesContainer $container */ 72 | if ($container) { 73 | $this->object->setProperties($container->getProperties('articles'),'articles'); 74 | } 75 | 76 | $this->isPublishing = $this->object->isDirty('published') && $this->object->get('published'); 77 | return $afterSave; 78 | } 79 | 80 | /** 81 | * Extend the saveTemplateVariables method and provide handling for the 'tags' type to store in a hidden TV 82 | * @return array|mixed 83 | */ 84 | public function saveTemplateVariables() { 85 | $saved = parent::saveTemplateVariables(); 86 | $tags = $this->getProperty('tags',null); 87 | if ($tags !== null) { 88 | /** @var modTemplateVar $tv */ 89 | $tv = $this->modx->getObject(modTemplateVar::class, [ 90 | 'name' => 'articlestags', 91 | ]); 92 | if ($tv) { 93 | $defaultValue = $tv->processBindings($tv->get('default_text'),$this->object->get('id')); 94 | if (strcmp($tags,$defaultValue) != 0) { 95 | /* update the existing record */ 96 | $tvc = $this->modx->getObject(modTemplateVarResource::class, [ 97 | 'tmplvarid' => $tv->get('id'), 98 | 'contentid' => $this->object->get('id'), 99 | ]); 100 | if ($tvc == null) { 101 | /** @var modTemplateVarResource $tvc add a new record */ 102 | $tvc = $this->modx->newObject(modTemplateVarResource::class); 103 | $tvc->set('tmplvarid',$tv->get('id')); 104 | $tvc->set('contentid',$this->object->get('id')); 105 | } 106 | $tvc->set('value',$tags); 107 | $tvc->save(); 108 | 109 | /* if equal to default value, erase TVR record */ 110 | } else { 111 | $tvc = $this->modx->getObject(modTemplateVarResource::class, [ 112 | 'tmplvarid' => $tv->get('id'), 113 | 'contentid' => $this->object->get('id'), 114 | ]); 115 | if (!empty($tvc)) { 116 | $tvc->remove(); 117 | } 118 | } 119 | } 120 | } 121 | return $saved; 122 | } 123 | 124 | /** 125 | * Set the friendly URL archive by forcing it into the URI. 126 | * @return bool|string 127 | */ 128 | public function setArchiveUri() { 129 | if (!$this->parentResource) { 130 | $this->parentResource = $this->object->getOne('Parent'); 131 | if (!$this->parentResource) { 132 | return false; 133 | } 134 | } 135 | 136 | return $this->object->setArchiveUri(); 137 | } 138 | 139 | public function afterSave() { 140 | $afterSave = parent::afterSave(); 141 | if ($this->isPublishing) { 142 | $this->object->notifyUpdateServices(); 143 | $this->object->sendNotifications(); 144 | } 145 | if($this->object->get('clearCache')) $this->clearContainerCache(); 146 | return $afterSave; 147 | } 148 | 149 | /** 150 | * Clears the container cache to ensure that the container listing is updated 151 | * @return void 152 | */ 153 | public function clearContainerCache() { 154 | $this->modx->cacheManager->refresh([ 155 | 'db' => [], 156 | 'auto_publish' => ['contexts' => [$this->object->get('context_key')]], 157 | 'context_settings' => ['contexts' => [$this->object->get('context_key')]], 158 | 'resource' => ['contexts' => [$this->object->get('context_key')]], 159 | ]); 160 | } 161 | 162 | /** 163 | * Override cleanup to send only back needed params 164 | * @return array|string 165 | */ 166 | public function cleanup() { 167 | $this->object->removeLock(); 168 | $this->clearCache(); 169 | 170 | $returnArray = $this->object->get(array_diff(array_keys($this->object->_fields), ['content','ta','introtext','description','link_attributes','pagetitle','longtitle','menutitle','articles_container_settings','properties'])); 171 | foreach ($returnArray as $k => $v) { 172 | if (strpos($k,'tv') === 0) { 173 | unset($returnArray[$k]); 174 | } 175 | if (strpos($k,'setting_') === 0) { 176 | unset($returnArray[$k]); 177 | } 178 | } 179 | $returnArray['class_key'] = $this->object->get('class_key'); 180 | $this->workingContext->prepare(true); 181 | $returnArray['preview_url'] = $this->modx->makeUrl($this->object->get('id'), $this->object->get('context_key'), '', 'full'); 182 | return $this->success('',$returnArray); 183 | } 184 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/ArticlesContainerCreateProcessor.php: -------------------------------------------------------------------------------- 1 | getProperties(); 24 | $settings = $this->object->getProperties('articles'); 25 | $notificationServices = []; 26 | foreach ($properties as $k => $v) { 27 | if (substr($k,0,8) == 'setting_') { 28 | $key = substr($k,8); 29 | if ($v === 'false') $v = 0; 30 | if ($v === 'true') $v = 1; 31 | 32 | switch ($key) { 33 | case 'notifyTwitter': 34 | if ($v) $notificationServices[] = 'twitter'; 35 | break; 36 | case 'notifyTwitterConsumerKey': 37 | if (!empty($v)) { 38 | $v = $this->object->encrypt($v); 39 | } 40 | break; 41 | case 'notifyTwitterConsumerKeySecret': 42 | if (!empty($v)) { 43 | $v = $this->object->encrypt($v); 44 | } 45 | break; 46 | case 'notifyFacebook': 47 | if ($v) $notificationServices[] = 'facebook'; 48 | break; 49 | } 50 | $settings[$key] = $v; 51 | } 52 | } 53 | $settings['notificationServices'] = implode(',',$notificationServices); 54 | $this->object->setProperties($settings,'articles'); 55 | 56 | $this->object->set('class_key',ArticlesContainer::class); 57 | $this->object->set('cacheable',true); 58 | $this->object->set('isfolder',true); 59 | return parent::beforeSave(); 60 | } 61 | 62 | /** 63 | * Override modResourceCreateProcessor::afterSave to provide custom functionality 64 | * {@inheritDoc} 65 | * @return boolean 66 | */ 67 | public function afterSave() { 68 | $this->addContainerId(); 69 | $this->removeFromArchivistIds(); 70 | $this->setProperty('clearCache',true); 71 | return parent::afterSave(); 72 | } 73 | 74 | /** 75 | * Add the Container ID to the articles system setting for managing container IDs for FURL redirection. 76 | * @return boolean 77 | */ 78 | public function addContainerId() { 79 | $saved = true; 80 | /** @var modSystemSetting $setting */ 81 | $setting = $this->modx->getObject(modSystemSetting::class, ['key' => 'articles.container_ids']); 82 | if (!$setting) { 83 | $setting = $this->modx->newObject(modSystemSetting::class); 84 | $setting->set('key','articles.container_ids'); 85 | $setting->set('namespace','articles'); 86 | $setting->set('area','furls'); 87 | $setting->set('xtype','textfield'); 88 | } 89 | $value = $setting->get('value'); 90 | $archiveKey = $this->object->get('id').':arc_'; 91 | $value = is_array($value) ? $value : explode(',',$value); 92 | if (!in_array($archiveKey,$value)) { 93 | $value[] = $archiveKey; 94 | $value = array_unique($value); 95 | $setting->set('value',implode(',',$value)); 96 | $saved = $setting->save(); 97 | } 98 | return $saved; 99 | } 100 | 101 | /** 102 | * Remove from Archivist IDs on prior versions of Archivist, to prevent conflicts 103 | * @return boolean 104 | */ 105 | public function removeFromArchivistIds() { 106 | $saved = true; 107 | /** @var modSystemSetting $setting */ 108 | $setting = $this->modx->getObject(modSystemSetting::class, ['key' => 'archivist.archive_ids']); 109 | if ($setting) { 110 | $value = $setting->get('value'); 111 | $archiveKey = $this->object->get('id').':arc_'; 112 | $value = is_array($value) ? $value : explode(',',$value); 113 | if (in_array($archiveKey,$value)) { 114 | $newKeys = []; 115 | foreach ($value as $k => $v) { 116 | if ($v == $archiveKey) continue; 117 | $newKeys[] = $v; 118 | } 119 | $newKeys = array_unique($newKeys); 120 | $setting->set('value',implode(',',$newKeys)); 121 | $saved = $setting->save(); 122 | } 123 | } 124 | return $saved; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /core/components/articles/src/Model/ArticlesContainerUpdateProcessor.php: -------------------------------------------------------------------------------- 1 | getProperties(); 21 | $properties['class_key'] = ArticlesContainer::class; 22 | $this->setProperties($properties); 23 | return $initialize; 24 | } 25 | 26 | /** 27 | * Override modResourceUpdateProcessor::beforeSave to provide custom functionality, saving settings for the container 28 | * to a custom field in the DB 29 | * {@inheritDoc} 30 | * @return boolean 31 | */ 32 | public function beforeSave() { 33 | $properties = $this->getProperties(); 34 | $settings = $this->object->getProperties('articles'); 35 | $notificationServices = []; 36 | foreach ($properties as $k => $v) { 37 | if (substr($k,0,8) == 'setting_') { 38 | $key = substr($k,8); 39 | if ($v === 'false') $v = 0; 40 | if ($v === 'true') $v = 1; 41 | 42 | switch ($key) { 43 | case 'notifyTwitter': 44 | if ($v) $notificationServices[] = 'twitter'; 45 | break; 46 | case 'notifyTwitterConsumerKey': 47 | if (!empty($v)) { 48 | $v = $this->object->encrypt($v); 49 | } 50 | break; 51 | case 'notifyTwitterConsumerKeySecret': 52 | if (!empty($v)) { 53 | $v = $this->object->encrypt($v); 54 | } 55 | break; 56 | case 'notifyFacebook': 57 | if ($v) $notificationServices[] = 'facebook'; 58 | break; 59 | } 60 | $settings[$key] = $v; 61 | } 62 | } 63 | $settings['notificationServices'] = implode(',',$notificationServices); 64 | $this->object->setProperties($settings,'articles'); 65 | return parent::beforeSave(); 66 | } 67 | 68 | /** 69 | * Override modResourceUpdateProcessor::afterSave to provide custom functionality 70 | * {@inheritDoc} 71 | * @return boolean 72 | */ 73 | public function afterSave() { 74 | $this->addContainerId(); 75 | $this->removeFromArchivistIds(); 76 | $this->setProperty('clearCache',true); 77 | //$this->object->set('isfolder',true); 78 | return parent::afterSave(); 79 | } 80 | 81 | /** 82 | * Add the Container ID to the articles system setting for managing IDs for FURL redirection. 83 | * @return boolean 84 | */ 85 | public function addContainerId() { 86 | $saved = true; 87 | /** @var modSystemSetting $setting */ 88 | $setting = $this->modx->getObject(modSystemSetting::class, ['key' => 'articles.container_ids']); 89 | if (!$setting) { 90 | $setting = $this->modx->newObject(modSystemSetting::class); 91 | $setting->set('key','articles.container_ids'); 92 | $setting->set('namespace','articles'); 93 | $setting->set('area','furls'); 94 | $setting->set('xtype','textfield'); 95 | } 96 | $value = $setting->get('value'); 97 | $archiveKey = $this->object->get('id').':arc_'; 98 | $value = is_array($value) ? $value : explode(',',$value); 99 | if (!in_array($archiveKey,$value)) { 100 | $value[] = $archiveKey; 101 | $value = array_unique($value); 102 | $setting->set('value',implode(',',$value)); 103 | $saved = $setting->save(); 104 | } 105 | return $saved; 106 | } 107 | 108 | /** 109 | * Remove from Archivist IDs on prior versions of Archivist, to prevent conflicts 110 | * @return boolean 111 | */ 112 | public function removeFromArchivistIds() { 113 | $saved = true; 114 | /** @var modSystemSetting $setting */ 115 | $setting = $this->modx->getObject(modSystemSetting::class, ['key' => 'archivist.archive_ids']); 116 | if ($setting) { 117 | $value = $setting->get('value'); 118 | $archiveKey = $this->object->get('id').':arc_'; 119 | $value = is_array($value) ? $value : explode(',',$value); 120 | if (in_array($archiveKey,$value)) { 121 | $newKeys = []; 122 | foreach ($value as $k => $v) { 123 | if ($v == $archiveKey) continue; 124 | $newKeys[] = $v; 125 | } 126 | $newKeys = array_unique($newKeys); 127 | $setting->set('value',implode(',',$newKeys)); 128 | $saved = $setting->save(); 129 | } 130 | } 131 | return $saved; 132 | } 133 | 134 | /** 135 | * Override cleanup to send only back needed params 136 | * @return array|string 137 | */ 138 | public function cleanup() { 139 | $this->object->removeLock(); 140 | $this->clearCache(); 141 | 142 | $returnArray = $this->object->get(array_diff(array_keys($this->object->_fields), ['content','ta','introtext','description','link_attributes','pagetitle','longtitle','menutitle','articles_container_settings','properties'])); 143 | foreach ($returnArray as $k => $v) { 144 | if (strpos($k,'tv') === 0) { 145 | unset($returnArray[$k]); 146 | } 147 | if (strpos($k,'setting_') === 0) { 148 | unset($returnArray[$k]); 149 | } 150 | } 151 | $returnArray['class_key'] = $this->object->get('class_key'); 152 | $this->workingContext->prepare(true); 153 | $returnArray['preview_url'] = $this->modx->makeUrl($this->object->get('id'), $this->object->get('context_key'), '', 'full'); 154 | return $this->success('',$returnArray); 155 | } 156 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/ArticlesRouter.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | namespace Articles\Model; 24 | 25 | use MODX\Revolution\modX; 26 | 27 | class ArticlesRouter { 28 | /** @var modX $modx */ 29 | public $modx; 30 | /** @var array $config */ 31 | public $config = []; 32 | 33 | function __construct(modX &$modx,array $config = []) { 34 | $this->modx =& $modx; 35 | $this->config = array_merge([ 36 | 37 | ],$config); 38 | } 39 | 40 | /** 41 | * Route the URL request based on the container IDs 42 | * @return boolean 43 | */ 44 | public function route() { 45 | $containerIds = $this->modx->getOption('articles.container_ids',null,''); 46 | if (empty($containerIds)) return false; 47 | $containerIds = explode(',',$containerIds); 48 | 49 | /* handle redirects */ 50 | $search = $_SERVER['REQUEST_URI']; 51 | $base_url = $this->modx->getOption('base_url'); 52 | if ($base_url != '/') { 53 | $search = str_replace($base_url,'',$search); 54 | } 55 | $search = trim($search, '/'); 56 | 57 | /* get resource to redirect to */ 58 | $resourceId = false; 59 | $prefix = 'arc_'; 60 | $startPageResId = false; 61 | $startPagePrefix = ''; 62 | $startPageId = $this->modx->getOption('site_start'); 63 | foreach ($containerIds as $archive) { 64 | if (empty($archive)) continue; 65 | $archive = explode(':',$archive); 66 | $archiveId = $archive[0]; 67 | 68 | if (method_exists($this->modx->context, 'getResourceURI')) { 69 | $alias = $this->modx->context->getResourceURI($archiveId); 70 | } else { 71 | $alias = is_array($this->modx->aliasMap) ? array_search($archiveId, $this->modx->aliasMap) : ''; 72 | } 73 | if ($alias && $startPageId == $archiveId) { 74 | $startPageResId = $archiveId; 75 | if (isset($archive[1])) $startPagePrefix = $archive[1]; 76 | } 77 | if ($alias && strpos($search, $alias) === 0) { 78 | $search = substr($search, strlen($alias)); 79 | $resourceId = $archiveId; 80 | if (isset($archive[1])) $prefix = $archive[1]; 81 | } 82 | } 83 | if (!$resourceId) { 84 | if ($startPageResId) { 85 | $resourceId = $startPageResId; 86 | $prefix = $startPagePrefix; 87 | } else return false; 88 | } 89 | if (!$resourceId) return false; 90 | 91 | $articlesContainer = $this->modx->getObject(ArticlesContainer::class, $resourceId); 92 | if ($articlesContainer instanceof ArticlesContainer && $articlesContainer->isRSS()) { 93 | $this->modx->sendForward($resourceId); 94 | return true; 95 | } 96 | 97 | /* figure out archiving */ 98 | $params = explode('/', $search); 99 | if (count($params) < 1) return false; 100 | 101 | /* tag handling! */ 102 | if ($params[0] == 'tags') { 103 | $_REQUEST[$prefix.'tag'] = $_GET['tag'] = urldecode($params[1]); 104 | /* author based */ 105 | } else if ($params[0] == 'user' || $params[0] == 'author') { 106 | $_REQUEST[$prefix.'author'] = $_GET[$prefix.'author'] = urldecode($params[1]); 107 | 108 | /* numeric "archives/1234" */ 109 | } else if ($params[0] == 'archives' && !empty($params[1])) { 110 | $resourceId = intval(trim(trim($params[1]),'/')); 111 | if (!empty($resourceId)) { 112 | $this->modx->sendForward($resourceId); 113 | } 114 | 115 | /* normal yyyy/mm/dd or yyyy/mm */ 116 | } else { 117 | /* set Archivist parameters for date-based archives */ 118 | 119 | if(is_numeric($params[0])) { 120 | $_REQUEST[$prefix.'year'] = $_GET[$prefix.'year'] = $params[0]; 121 | if (isset($params[1]) && is_numeric($params[1])) { 122 | $_REQUEST[$prefix.'month'] = $_GET[$prefix.'month'] = $params[1]; 123 | } else if(isset($params[1])) { 124 | // Display the default 404 page if "month" is not a number 125 | $this->modx->sendForward($this->modx->getOption('error_page'), 'HTTP/1.1 404 Not Found'); 126 | } 127 | if (isset($params[2]) && is_numeric($params[2])) { 128 | $_REQUEST[$prefix.'day'] = $_GET[$prefix.'day'] = $params[2]; 129 | } else if(isset($params[2])) { 130 | // Display the default 404 page if "day" is not a number 131 | $this->modx->sendForward($this->modx->getOption('error_page'), 'HTTP/1.1 404 Not Found'); 132 | 133 | } 134 | } else { 135 | // Display the default 404 page if nothing found 136 | $this->modx->sendForward($this->modx->getOption('error_page'), 'HTTP/1.1 404 Not Found'); 137 | } 138 | 139 | } 140 | 141 | /* forward */ 142 | $this->modx->sendForward($resourceId); 143 | return true; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /core/components/articles/src/Model/Import/ArticlesImport.php: -------------------------------------------------------------------------------- 1 | articles =& $articles; 28 | $this->processor =& $processor; 29 | $this->modx =& $articles->modx; 30 | $this->config = array_merge([ 31 | 32 | ],$config); 33 | if (!empty($this->config['id'])) { 34 | $this->config['id'] = trim(trim($this->config['id'],'#')); 35 | } 36 | 37 | $this->initialize(); 38 | } 39 | 40 | /** 41 | * Initialize the importer and load the Quip package 42 | */ 43 | public function initialize() { 44 | @set_time_limit(0); 45 | @ini_set('memory_limit','1024M'); 46 | $quipPath = $this->modx->getOption('quip.core_path',null,$this->modx->getOption('core_path').'components/quip/'); 47 | $this->modx->addPackage('quip',$quipPath.'model/'); 48 | } 49 | 50 | /** 51 | * Abstract method that is called to import from a specific service. 52 | * @abstract 53 | * @return boolean 54 | */ 55 | abstract public function import(); 56 | 57 | /** 58 | * Add an error to the response 59 | * @param string $field 60 | * @param string $message 61 | * @return mixed 62 | */ 63 | public function addError($field,$message = '') { 64 | return $this->processor->addFieldError($field,$message); 65 | } 66 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/Import/ArticlesImportModx.php: -------------------------------------------------------------------------------- 1 | container = $this->modx->getObject(ArticlesContainer::class,$this->config['id']); 25 | 26 | $c = $this->getQuery(); 27 | if ($c === false) return $imported; 28 | 29 | $resources = $this->modx->getIterator(modResource::class,$c); 30 | if (empty($resources)) { 31 | $this->processor->addFieldError('parents','No resources found!'); 32 | return false; 33 | } 34 | foreach ($resources as $resource) { 35 | $imported = $this->importResource($resource); 36 | } 37 | return $imported; 38 | } 39 | 40 | public function getQuery() { 41 | $c = $this->modx->newQuery(modResource::class); 42 | $c->select($this->modx->getSelectColumns(modResource::class,'modResource')); 43 | $where = []; 44 | 45 | /* parents */ 46 | $ids = []; 47 | if (!empty($this->config['modx-parents'])) { 48 | $parents = is_array($this->config['modx-parents']) ? $this->config['modx-parents'] : explode(',',$this->config['modx-parents']); 49 | foreach ($parents as $parent) { 50 | /** @var modResource $parentResource */ 51 | $parentResource = $this->modx->getObject(modResource::class,$parent); 52 | if (!$parentResource) continue; 53 | 54 | $children = $this->modx->getChildIds($parent,10, [ 55 | 'context' => $parentResource->get('context_key'), 56 | ]); 57 | $ids = array_merge($ids,$children); 58 | } 59 | } 60 | 61 | /* specific resources */ 62 | $exclude = []; 63 | $include = []; 64 | if (!empty($this->config['modx-resources'])) { 65 | $resources = is_array($this->config['modx-resources']) ? $this->config['modx-resources'] : explode(',',$this->config['modx-resources']); 66 | foreach ($resources as $resourceId) { 67 | if (strpos($resourceId,'-') === 0) { 68 | $exclude[] = intval(substr($resourceId,1)); 69 | } else { 70 | $include[] = intval($resourceId); 71 | } 72 | } 73 | } 74 | $ids = array_merge($ids,$include); 75 | sort($ids); 76 | $ids = array_unique($ids); 77 | if (!empty($ids)) { 78 | $where['id:IN'] = $ids; 79 | } 80 | 81 | $exclude = array_unique($exclude); 82 | if (!empty($exclude)) { 83 | $where['id:NOT IN'] = $exclude; 84 | } 85 | 86 | /* template */ 87 | if (!empty($this->config['modx-template'])) { 88 | $where['template'] = $this->config['modx-template']; 89 | } 90 | 91 | if (isset($this->config['modx-unpublished']) && empty($this->config['modx-unpublished'])) { 92 | $where['published'] = 1; 93 | } 94 | 95 | if (isset($this->config['modx-hidemenu']) && empty($this->config['modx-hidemenu'])) { 96 | $where['hidemenu'] = 0; 97 | } 98 | 99 | if (empty($where)) { 100 | $this->addError('modx-parents',$this->modx->lexicon('articles.import_modx_err_no_criteria')); 101 | return false; 102 | } 103 | 104 | /* dont let them get the site start */ 105 | $where['id:!='] = [(int)$this->modx->getOption('site_start',null,1)]; 106 | 107 | $where['isfolder'] = false; 108 | $where['class_key:!='] = Article::class; 109 | $c->where($where); 110 | 111 | if (!empty($this->config['modx-tagsField'])) { 112 | $this->getTagsQuery($c); 113 | } 114 | return $c; 115 | } 116 | 117 | /** 118 | * Get the nice little query to get the tags field 119 | * @param xPDOQuery $c 120 | */ 121 | public function getTagsQuery(xPDOQuery &$c) { 122 | $tagsField = $this->config['modx-tagsField']; 123 | $isTV = true; 124 | if (intval($tagsField) > 0) { 125 | $tagsField = ['id' => $tagsField]; 126 | } else { 127 | if (strpos($tagsField,'tv.') === 0) { 128 | $tagsField = ['name' => str_replace('tv.','',$tagsField)]; 129 | } else { 130 | $isTV = false; 131 | } 132 | } 133 | 134 | if ($isTV) { 135 | /** @var modTemplateVar $tv */ 136 | $tv = $this->modx->getObject(modTemplateVar::class,$tagsField); 137 | if ($tv) { 138 | $c->leftJoin(modTemplateVarResource::class,'Tags', [ 139 | 'Tags.contentid = modResource.id', 140 | 'Tags.tmplvarid' => $tv->get('id'), 141 | ]); 142 | $c->select([ 143 | 'tags' => 'Tags.value', 144 | ]); 145 | } 146 | } else { 147 | $c->select([ 148 | 'tags' => $tagsField, 149 | ]); 150 | } 151 | } 152 | 153 | /** 154 | * Import the Resource into Articles 155 | * 156 | * @param modResource $resource 157 | * @return boolean 158 | */ 159 | public function importResource(modResource $resource) { 160 | $resource->set('searchable',true); 161 | $resource->set('richtext',true); 162 | $resource->set('isfolder',false); 163 | $resource->set('cacheable',true); 164 | $resource->set('class_key',Article::class); 165 | $resource->set('parent',$this->container->get('id')); 166 | $settings = $this->container->getProperties('articles'); 167 | $resource->setProperties($settings,'articles'); 168 | 169 | if (!empty($this->config['modx-change-template'])) { 170 | $resource->set('template',$settings['articleTemplate']); 171 | } 172 | 173 | $this->setResourceUri($resource); 174 | if (!empty($this->config['modx-commentsThreadNameFormat'])) { 175 | $this->importComments($resource); 176 | } 177 | 178 | $saved = true; 179 | if (!$this->debug) { 180 | $saved = $resource->save(); 181 | if ($saved) { 182 | $resource->setTVValue('articlestags',$resource->get('tags')); 183 | } 184 | } 185 | 186 | return $saved; 187 | } 188 | 189 | /** 190 | * Set the new Articles-based URI 191 | * @param modResource $resource 192 | */ 193 | public function setResourceUri(modResource &$resource) { 194 | $date = $resource->get('published') ? $resource->get('publishedon') : $resource->get('createdon'); 195 | $year = date('Y',strtotime($date)); 196 | $month = date('m',strtotime($date)); 197 | $day = date('d',strtotime($date)); 198 | 199 | $containerUri = $this->container->get('uri'); 200 | if (empty($containerUri)) { 201 | $containerUri = $this->container->get('alias'); 202 | } 203 | $uri = rtrim($containerUri,$this->modx->getOption('container_suffix', null, '/')).'/'.$year.'/'.$month.'/'.$day.'/'.$resource->get('alias'); 204 | 205 | $resource->set('uri',rtrim($uri,'/').'/'); 206 | $resource->set('uri_override',true); 207 | } 208 | 209 | /** 210 | * If set, import any comments from Quip 211 | * @param modResource $resource 212 | * @return boolean 213 | */ 214 | public function importComments(modResource &$resource) { 215 | $threadFormat = $this->config['modx-commentsThreadNameFormat']; 216 | if (empty($threadFormat)) return true; 217 | 218 | $imported = true; 219 | $threadFormat = str_replace(['[[*id]]','[[+id]]'],$resource->get('id'),$threadFormat); 220 | /** @var quipThread $thread */ 221 | $thread = $this->modx->getObject(quipThread::class, ['name' => $threadFormat]); 222 | if ($thread) { 223 | $newThreadName = 'article-b'.$this->container->get('id').'-'.$resource->get('id'); 224 | 225 | $sql = 'UPDATE '.$this->modx->getTableName(quipComment::class) 226 | .' SET '.$this->modx->escape('thread').' = "'.$newThreadName.'"' 227 | .' WHERE '.$this->modx->escape('thread').' = "'.$thread->get('name').'"'; 228 | if (!$this->debug) { 229 | $this->modx->exec($sql); 230 | } 231 | 232 | $sql = 'UPDATE '.$this->modx->getTableName(quipThread::class) 233 | .' SET '.$this->modx->escape('name').' = "'.$newThreadName.'"' 234 | .' WHERE '.$this->modx->escape('name').' = "'.$thread->get('name').'"'; 235 | if (!$this->debug) { 236 | $this->modx->exec($sql); 237 | } 238 | $imported = true; 239 | } else { 240 | $this->modx->log(modX::LOG_LEVEL_ERROR,'[Articles] Could not find Quip Thread with thread name: '.$threadFormat); 241 | } 242 | return $imported; 243 | } 244 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/Notification/ArticlesNotification.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | namespace Articles\Model\Notification; 24 | 25 | use Articles\Model\Article; 26 | use MODX\Revolution\modX; 27 | 28 | /** 29 | * Base abstract class for notification senders 30 | * 31 | * @package articles 32 | * @subpackage notifications 33 | */ 34 | abstract class ArticlesNotification { 35 | /** @var modX $xpdo */ 36 | public $modx; 37 | /** @var Article $article */ 38 | public $article; 39 | /** @var array $config */ 40 | public $config = []; 41 | 42 | function __construct(Article $article,array $config = []) { 43 | $this->article =& $article; 44 | $this->modx =& $article->xpdo; 45 | $this->config = array_merge([ 46 | 47 | ],$config); 48 | } 49 | 50 | /** 51 | * @abstract 52 | * @param string $title The title of the Article 53 | * @param string $url The full URL of the Article 54 | * @return boolean 55 | */ 56 | abstract public function send($title,$url); 57 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/Notification/ArticlesNotificationTwitter.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Articles is free software; you can redistribute it and/or modify it under the 8 | * terms of the GNU General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) any later 10 | * version. 11 | * 12 | * Articles is distributed in the hope that it will be useful, but WITHOUT ANY 13 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * Articles; if not, write to the Free Software Foundation, Inc., 59 Temple 18 | * Place, Suite 330, Boston, MA 02111-1307 USA 19 | * 20 | * @package articles 21 | */ 22 | 23 | namespace Articles\Model\Notification; 24 | 25 | use Articles\Model\Article; 26 | use Articles\Model\ArticlesContainer; 27 | use MODX\Revolution\modX; 28 | use SimpleXMLElement; 29 | 30 | /** 31 | * Posts titles, URLs and tags for new Articles to Twitter 32 | * @package articles 33 | * @subpackage twitter 34 | */ 35 | class ArticlesNotificationTwitter extends ArticlesNotification { 36 | 37 | public function initialize() { 38 | $this->config = array_merge($this->config,$this->article->getContainerSettings()); 39 | } 40 | 41 | public function send($title, $url) { 42 | $this->initialize(); 43 | $url = $this->shorten($url); 44 | $message = $this->getMessage($title,$url); 45 | return $this->update($message); 46 | } 47 | 48 | public function update($message) { 49 | /** @var ArticlesContainer $container */ 50 | $container = $this->modx->getObject(ArticlesContainer::class,$this->article->get('parent')); 51 | if (!$container) return false; 52 | 53 | $keys = $container->getTwitterKeys(); 54 | $accessToken = $container->decrypt($this->config['notifyTwitterAccessToken']); 55 | $accessTokenSecret = $container->decrypt($this->config['notifyTwitterAccessTokenSecret']); 56 | $connection = new TwitterOAuth($keys['consumer_key'],$keys['consumer_key_secret'],$accessToken,$accessTokenSecret); 57 | $output = $connection->post('statuses/update', ['status' => $message]); 58 | return $output; 59 | } 60 | 61 | public function getMessage($title,$url) { 62 | $defaultTpl = '[[+title]] [[+url]] [[+hashtags]]'; 63 | $defaultTplLength = strlen($defaultTpl); 64 | $tpl = $this->modx->getOption('notifyTwitterTpl',$this->config,$defaultTpl); 65 | $encoding = $this->modx->getOption('modx_charset',null,'UTF-8'); 66 | $useMb = $this->modx->getOption('use_multibyte',null,false) && function_exists('mb_strlen'); 67 | 68 | $preProcessedLength = $useMb ? mb_strlen($tpl,$encoding) : strlen($tpl); 69 | $preProcessedLength = $preProcessedLength - $defaultTplLength + 2; /* subtract placeholders */ 70 | if ($preProcessedLength < 1 || $preProcessedLength > 140) { 71 | $tpl = $defaultTpl; 72 | $preProcessedLength = 2; 73 | } 74 | 75 | $tags = $this->article->getTVValue('articlestags'); 76 | $tags = explode(',',$tags); 77 | $hashTags = []; 78 | $tagLimit = $this->modx->getOption('notifyTwitterTagLimit',$this->config,3); 79 | $badTagChars = [' ','#','$','*','@','(',')','[',']','=','!','?',';',',','.']; 80 | $i = 1; 81 | foreach ($tags as $tag) { 82 | if ($i > $tagLimit) break; 83 | $hashTags[] = '#'.str_replace($badTagChars,'',strtolower(trim(ltrim($tag,'#')))); 84 | $i++; 85 | } 86 | $hashTags = implode(' ',$hashTags); 87 | 88 | $titleLength = $useMb ? mb_strlen($title,$encoding) : strlen($title); 89 | $urlLength = $useMb ? mb_strlen($url,$encoding) : strlen($url); 90 | $hashTagsLength = $useMb ? mb_strlen($hashTags,$encoding) : strlen($hashTags); 91 | $processedLength = $preProcessedLength + $titleLength + $urlLength + $hashTagsLength; 92 | 93 | if ($processedLength > 140) { 94 | $extraChars = 140 - $processedLength; 95 | $l = ($extraChars + 3) * -1; 96 | $title = $useMb ? mb_substr($title,0,$l,$encoding) : substr($title,0,$l); 97 | $title .= '...'; 98 | } 99 | 100 | return str_replace([ 101 | '[[+title]]', 102 | '[[+url]]', 103 | '[[+hashtags]]', 104 | ], [ 105 | $title, 106 | $url, 107 | $hashTags, 108 | ],$tpl); 109 | } 110 | 111 | public function shorten($url) { 112 | $shorteningService = $this->modx->getOption('shorteningService',$this->config,'tinyurl'); 113 | if (!empty($shorteningService)) { 114 | $className = ArticlesShortener::class.ucfirst(strtolower($shorteningService)); 115 | if (class_exists($className)) { 116 | /** @var ArticlesTwitterShortener $shortener */ 117 | $shortener = new $className($this); 118 | $url = $shortener->shorten($url,$this->config); 119 | } 120 | } 121 | return $url; 122 | } 123 | } 124 | 125 | /** 126 | * Abstract base class for shortening classes. Thanks to Kyle Jaebker (muddydogpaws.com) for the inspiration. 127 | * 128 | * @package articles 129 | * @subpackage twitter 130 | */ 131 | abstract class ArticlesTwitterShortener { 132 | /** @var modX $modx */ 133 | public $modx; 134 | /** @var Article $article */ 135 | public $article; 136 | /** @var ArticlesNotificationTwitter $notifier */ 137 | public $notifier; 138 | /** @var array $config */ 139 | public $config = []; 140 | 141 | function __construct(ArticlesNotificationTwitter $notifier,array $settings = []) { 142 | $this->notifier =& $notifier; 143 | $this->modx =& $notifier->modx; 144 | $this->article =& $notifier->article; 145 | $this->config = array_merge($this->config,$settings); 146 | } 147 | 148 | /** 149 | * @param string $url 150 | * @return array 151 | */ 152 | abstract protected function getParameters($url); 153 | abstract protected function getServiceUrl(); 154 | 155 | /** 156 | * @param string $url 157 | * @return mixed 158 | */ 159 | public function shorten($url) { 160 | $response = null; 161 | $userAgent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6'; 162 | $parameters = $this->getParameters($url); 163 | $serviceUrl = $this->getServiceUrl(); 164 | $requestUrl = $this->prepareRequestUrl($serviceUrl,$parameters); 165 | 166 | if (!empty($requestUrl)) { 167 | if (function_exists('curl_init')) { 168 | $ch = curl_init(); 169 | $options = [ 170 | CURLOPT_URL => $requestUrl, 171 | CURLOPT_RETURNTRANSFER => true, 172 | CURLOPT_USERAGENT => $userAgent, 173 | CURLOPT_TIMEOUT => 30, 174 | ]; 175 | if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) $options[CURLOPT_FOLLOWLOCATION] = true; 176 | $options[CURLOPT_RETURNTRANSFER] = true; 177 | $this->prepareCurlOptions($options); 178 | curl_setopt_array($ch, $options); 179 | $response = curl_exec($ch); 180 | curl_close($ch); 181 | } else { 182 | @ini_set('user_agent',$userAgent); 183 | $response = file_get_contents($requestUrl); 184 | } 185 | } 186 | return $this->parseResponse($response,$url); 187 | } 188 | 189 | protected function prepareRequestUrl($serviceUrl,array $parameters = []) { 190 | $query = http_build_query($parameters); 191 | if (strpos($serviceUrl,'?') === false) { 192 | $query = '?'.$query; 193 | } 194 | return $serviceUrl.$query; 195 | } 196 | protected function prepareCurlOptions(array $options = []) { 197 | return $options; 198 | } 199 | 200 | protected function parseResponse($response,$longUrl) { 201 | return $response; 202 | } 203 | } 204 | 205 | /** 206 | * @package articles 207 | * @subpackage twitter 208 | * @deprecated Apparently bit.ly requires OAuth now; that's gonna be a pain to sort out; delaying for a further release. 209 | */ 210 | class ArticlesShortenerBitly extends ArticlesTwitterShortener { 211 | protected function getServiceUrl() { 212 | return $this->modx->getOption('articles.bitly.api_url',null,'http://api.bit.ly/shorten'); 213 | } 214 | 215 | protected function getParameters($url) { 216 | return [ 217 | 'version' => '2.0.1', 218 | 'login' => $this->modx->getOption('bitlyUsername',$this->config,''), 219 | 'apiKey' => $this->modx->getOption('bitlyApiKey',$this->config,''), 220 | 'longUrl' => $url, 221 | 'format' => 'xml' 222 | ]; 223 | } 224 | 225 | protected function parseResponse($response,$longUrl) { 226 | /** @var SimpleXmlElement $xml */ 227 | $xml = simplexml_load_string($response); 228 | $this->modx->log(modX::LOG_LEVEL_ERROR,print_r($xml,true)); 229 | if ($xml->errorCode == 0) { 230 | $url = $xml->results->nodeKeyVal->shortUrl; 231 | } else { 232 | $url = $longUrl; 233 | } 234 | return $url; 235 | } 236 | } 237 | 238 | class ArticlesShortenerTinyurl extends ArticlesTwitterShortener { 239 | protected function getServiceUrl() { 240 | return $this->modx->getOption('articles.tinyurl.api_url',null,'http://tinyurl.com/api-create.php'); 241 | } 242 | protected function getParameters($url) { 243 | return [ 244 | 'url' => $url, 245 | ]; 246 | } 247 | } 248 | 249 | class ArticlesShortenerIsgd extends ArticlesTwitterShortener { 250 | protected function getServiceUrl() { 251 | return $this->modx->getOption('articles.isgd.api_url',null,'http://is.gd/api.php'); 252 | } 253 | protected function getParameters($url) { 254 | return [ 255 | 'longurl' => $url 256 | ]; 257 | } 258 | } 259 | 260 | class ArticlesShortenerDigg extends ArticlesTwitterShortener { 261 | public function getServiceUrl() { 262 | return $this->modx->getOption('articles.digg.api_url',null,'http://services.digg.com/url/short/create'); 263 | } 264 | 265 | protected function getParameters($url) { 266 | return [ 267 | 'url' => $url, 268 | 'appkey' => $this->modx->getOption('articles.digg.app_key',null,'http://modx.com'), 269 | 'type' => 'xml', 270 | ]; 271 | } 272 | 273 | protected function parseResponse($response,$longUrl) { 274 | $xml = simplexml_load_string($response); 275 | $url = $longUrl; 276 | if ($xml) { 277 | foreach($xml->shorturl->attributes() as $a => $b) { 278 | if ($a == 'short_url') { 279 | $url = $b; 280 | break; 281 | } 282 | } 283 | } 284 | return $url; 285 | } 286 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/Notification/TwitterOAuth.php: -------------------------------------------------------------------------------- 1 | http_status; } 56 | function lastAPICall() { return $this->last_api_call; } 57 | 58 | /** 59 | * construct TwitterOAuth object 60 | */ 61 | function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { 62 | $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); 63 | $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); 64 | if (!empty($oauth_token) && !empty($oauth_token_secret)) { 65 | $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); 66 | } else { 67 | $this->token = NULL; 68 | } 69 | } 70 | 71 | 72 | /** 73 | * Get a request_token from Twitter 74 | * 75 | * @returns a key/value array containing oauth_token and oauth_token_secret 76 | */ 77 | function getRequestToken($oauth_callback = NULL) { 78 | $parameters = []; 79 | if (!empty($oauth_callback)) { 80 | $parameters['oauth_callback'] = $oauth_callback; 81 | } 82 | $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); 83 | $token = OAuthUtil::parse_parameters($request); 84 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 85 | return $token; 86 | } 87 | 88 | /** 89 | * Get the authorize URL 90 | * 91 | * @returns a string 92 | */ 93 | function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { 94 | if (is_array($token)) { 95 | $token = $token['oauth_token']; 96 | } 97 | if (empty($sign_in_with_twitter)) { 98 | return $this->authorizeURL() . "?oauth_token={$token}"; 99 | } else { 100 | return $this->authenticateURL() . "?oauth_token={$token}"; 101 | } 102 | } 103 | 104 | /** 105 | * Exchange request token and secret for an access token and 106 | * secret, to sign API calls. 107 | * 108 | * @returns array("oauth_token" => "the-access-token", 109 | * "oauth_token_secret" => "the-access-secret", 110 | * "user_id" => "9436992", 111 | * "screen_name" => "abraham") 112 | */ 113 | function getAccessToken($oauth_verifier = FALSE) { 114 | $parameters = []; 115 | if (!empty($oauth_verifier)) { 116 | $parameters['oauth_verifier'] = $oauth_verifier; 117 | } 118 | $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); 119 | $token = OAuthUtil::parse_parameters($request); 120 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 121 | return $token; 122 | } 123 | 124 | /** 125 | * One time exchange of username and password for access token and secret. 126 | * 127 | * @returns array("oauth_token" => "the-access-token", 128 | * "oauth_token_secret" => "the-access-secret", 129 | * "user_id" => "9436992", 130 | * "screen_name" => "abraham", 131 | * "x_auth_expires" => "0") 132 | */ 133 | function getXAuthToken($username, $password) { 134 | $parameters = []; 135 | $parameters['x_auth_username'] = $username; 136 | $parameters['x_auth_password'] = $password; 137 | $parameters['x_auth_mode'] = 'client_auth'; 138 | $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); 139 | $token = OAuthUtil::parse_parameters($request); 140 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 141 | return $token; 142 | } 143 | 144 | /** 145 | * GET wrapper for oAuthRequest. 146 | */ 147 | function get($url, $parameters = []) { 148 | $response = $this->oAuthRequest($url, 'GET', $parameters); 149 | if ($this->format === 'json' && $this->decode_json) { 150 | return json_decode($response); 151 | } 152 | return $response; 153 | } 154 | 155 | /** 156 | * POST wrapper for oAuthRequest. 157 | */ 158 | function post($url, $parameters = []) { 159 | $response = $this->oAuthRequest($url, 'POST', $parameters); 160 | if ($this->format === 'json' && $this->decode_json) { 161 | return json_decode($response); 162 | } 163 | return $response; 164 | } 165 | 166 | /** 167 | * DELETE wrapper for oAuthReqeust. 168 | */ 169 | function delete($url, $parameters = []) { 170 | $response = $this->oAuthRequest($url, 'DELETE', $parameters); 171 | if ($this->format === 'json' && $this->decode_json) { 172 | return json_decode($response); 173 | } 174 | return $response; 175 | } 176 | 177 | /** 178 | * Format and sign an OAuth / API request 179 | */ 180 | function oAuthRequest($url, $method, $parameters) { 181 | if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { 182 | $url = "{$this->host}{$url}.{$this->format}"; 183 | } 184 | $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); 185 | $request->sign_request($this->sha1_method, $this->consumer, $this->token); 186 | switch ($method) { 187 | case 'GET': 188 | return $this->http($request->to_url(), 'GET'); 189 | default: 190 | return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); 191 | } 192 | } 193 | 194 | /** 195 | * Make an HTTP request 196 | * 197 | * @return API results 198 | */ 199 | function http($url, $method, $postfields = NULL) { 200 | $this->http_info = []; 201 | $ci = curl_init(); 202 | /* Curl settings */ 203 | curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); 204 | curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); 205 | curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); 206 | curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); 207 | curl_setopt($ci, CURLOPT_HTTPHEADER, ['Expect:']); 208 | curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); 209 | curl_setopt($ci, CURLOPT_HEADERFUNCTION, [$this, 'getHeader']); 210 | curl_setopt($ci, CURLOPT_HEADER, FALSE); 211 | 212 | switch ($method) { 213 | case 'POST': 214 | curl_setopt($ci, CURLOPT_POST, TRUE); 215 | if (!empty($postfields)) { 216 | curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); 217 | } 218 | break; 219 | case 'DELETE': 220 | curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); 221 | if (!empty($postfields)) { 222 | $url = "{$url}?{$postfields}"; 223 | } 224 | } 225 | 226 | curl_setopt($ci, CURLOPT_URL, $url); 227 | $response = curl_exec($ci); 228 | $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); 229 | $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); 230 | $this->url = $url; 231 | curl_close ($ci); 232 | return $response; 233 | } 234 | 235 | /** 236 | * Get the header info to store. 237 | */ 238 | function getHeader($ch, $header) { 239 | $i = strpos($header, ':'); 240 | if (!empty($i)) { 241 | $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); 242 | $value = trim(substr($header, $i + 2)); 243 | $this->http_header[$key] = $value; 244 | } 245 | return strlen($header); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /core/components/articles/src/Model/Update/ArticlesPingomatic.php: -------------------------------------------------------------------------------- 1 | '. 18 | ''. 19 | ' weblogUpdates.ping'. 20 | ' '. 21 | ' '. 22 | ' '.$title.''. 23 | ' '. 24 | ' '. 25 | ' '.$url.''. 26 | ' '. 27 | ' '. 28 | ''; 29 | $this->prepareQuery($request); 30 | $result = $this->query(); 31 | if (empty($result)) { 32 | $this->modx->log(modX::LOG_LEVEL_ERROR,'Could not connect to pingomatic!'); 33 | return false; 34 | } 35 | return $this->processResult($result,$title,$url); 36 | } 37 | 38 | public function prepareQuery($request) { 39 | $server = $this->modx->getOption('articles.pingomatic_server',null,'http://rpc.pingomatic.com/'); 40 | $this->ch = curl_init(); 41 | curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true); 42 | curl_setopt($this->ch, CURLOPT_URL, $server); 43 | curl_setopt($this->ch, CURLOPT_HTTPHEADER, 44 | [ 45 | 'Content-type: text/xml', 46 | 'Content-length: '.strlen($request), 47 | 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729', 48 | ] 49 | ); 50 | curl_setopt($this->ch, CURLOPT_POST, true); 51 | curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request); 52 | return $this->ch; 53 | } 54 | 55 | public function query() { 56 | return curl_exec($this->ch); 57 | } 58 | 59 | public function processResult($result,$title,$url) { 60 | $success = false; 61 | try { 62 | /** @var SimpleXMLElement $xml */ 63 | $xml = simplexml_load_string($result); 64 | if ($xml->params && $xml->params->param && $xml->params->param->value && $xml->params->param->value->struct && $xml->params->param->value->struct->member && $xml->params->param->value->struct->member[0]) { 65 | $errorCode = $xml->params->param->value->struct->member[0]; 66 | $errorMessage = $xml->params->param->value->struct->member[1]; 67 | if ((string)$errorCode->value->boolean == '1') { 68 | $this->modx->log(modX::LOG_LEVEL_ERROR,'[Articles] Pingomatic error: '.$errorMessage->value->string); 69 | } else { 70 | $this->modx->log(modX::LOG_LEVEL_INFO,'[Articles] Sent Ping-o-matic request for "'.$title.'" at URL: '.$url); 71 | $success = true; 72 | } 73 | } 74 | } catch (Exception $e) { 75 | $this->modx->log(modX::LOG_LEVEL_ERROR,'[Articles] '.$e->getMessage()); 76 | } 77 | return $success; 78 | } 79 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/Update/ArticlesUpdateService.php: -------------------------------------------------------------------------------- 1 | article =& $article; 22 | $this->modx =& $article->xpdo; 23 | $this->config = array_merge([ 24 | 25 | ],$config); 26 | } 27 | 28 | /** 29 | * @abstract 30 | * @param string $title The title of the Article 31 | * @param string $url The full URL of the Article 32 | */ 33 | abstract public function notify($title,$url); 34 | } -------------------------------------------------------------------------------- /core/components/articles/src/Model/metadata.mysql.php: -------------------------------------------------------------------------------- 1 | '3.0', 4 | 'namespace' => 'Articles\\Model', 5 | 'namespacePrefix' => 'Articles', 6 | 'class_map' => 7 | array ( 8 | 'MODX\\Revolution\\modResource' => 9 | array ( 10 | 0 => 'Articles\\Model\\ArticlesContainer', 11 | 1 => 'Articles\\Model\\Article', 12 | ), 13 | ), 14 | ); -------------------------------------------------------------------------------- /core/components/articles/src/Model/mysql/Article.php: -------------------------------------------------------------------------------- 1 | 'Articles\\Model', 11 | 'version' => '3.0', 12 | 'extends' => 'MODX\\Revolution\\modResource', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'InnoDB', 16 | ), 17 | 'fields' => 18 | array ( 19 | ), 20 | 'fieldMeta' => 21 | array ( 22 | ), 23 | 'aggregates' => 24 | array ( 25 | 'Container' => 26 | array ( 27 | 'class' => 'Articles\\Model\\ArticlesContainer', 28 | 'local' => 'parent', 29 | 'foreign' => 'id', 30 | 'cardinality' => 'one', 31 | 'owner' => 'foreign', 32 | ), 33 | ), 34 | ); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/components/articles/src/Model/mysql/ArticlesContainer.php: -------------------------------------------------------------------------------- 1 | 'Articles\\Model', 11 | 'version' => '3.0', 12 | 'extends' => 'MODX\\Revolution\\modResource', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'InnoDB', 16 | ), 17 | 'fields' => 18 | array ( 19 | ), 20 | 'fieldMeta' => 21 | array ( 22 | ), 23 | 'composites' => 24 | array ( 25 | 'Article' => 26 | array ( 27 | 'class' => 'Articles\\Model\\Article', 28 | 'local' => 'id', 29 | 'foreign' => 'parent', 30 | 'cardinality' => 'many', 31 | 'owner' => 'local', 32 | ), 33 | ), 34 | ); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/DeleteMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids',null); 19 | if (empty($ids)) { 20 | return $this->failure($this->modx->lexicon('articles.articles_err_ns_multiple')); 21 | } 22 | $ids = is_array($ids) ? $ids : explode(',',$ids); 23 | 24 | foreach ($ids as $id) { 25 | if (empty($id)) continue; 26 | $this->modx->runProcessor('resource/delete', [ 27 | 'id' => $id, 28 | ]); 29 | } 30 | return $this->success(); 31 | } 32 | } 33 | return DeleteMultiple::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/GetList.php: -------------------------------------------------------------------------------- 1 | editAction = 'resource/update'; 39 | if ($this->modx->getVersionData()['version'] < 3) { 40 | $action = $this->modx->getObject('modAction', [ 41 | 'namespace' => 'core', 42 | 'controller' => 'resource/update', 43 | ]); 44 | if ($action) { 45 | $this->editAction = $action->get('id'); 46 | } 47 | } 48 | 49 | $this->defaultSortField = $this->modx->getOption('articles.default_article_sort_field',null,'createdon'); 50 | 51 | if ($this->getParentContainer()) { 52 | $settings = $this->container->getContainerSettings(); 53 | if ($this->modx->getOption('commentsEnabled',$settings,false)) { 54 | $quipCorePath = $this->modx->getOption('quip.core_path',null,$this->modx->getOption('core_path',null,MODX_CORE_PATH).'components/quip/'); 55 | if ($this->modx->addPackage('quip',$quipCorePath.'model/')) { 56 | $this->commentsEnabled = true; 57 | } 58 | } 59 | } 60 | return parent::initialize(); 61 | } 62 | 63 | public function getTagsTV() { 64 | $this->tvTags = $this->modx->getObject(modTemplateVar::class, ['name' => 'articlestags']); 65 | if (!$this->tvTags && $this->getProperty('sort') == 'tags') { 66 | $this->setProperty('sort','createdon'); 67 | } 68 | return $this->tvTags; 69 | } 70 | 71 | public function getParentContainer() { 72 | $parent = $this->getProperty('parent'); 73 | if (!empty($parent)) { 74 | $this->container = $this->modx->getObject(ArticlesContainer::class,$parent); 75 | } 76 | return $this->container; 77 | } 78 | 79 | public function prepareQueryBeforeCount(\xPDO\Om\xPDOQuery $c) { 80 | $c->leftJoin(modUser::class,'CreatedBy'); 81 | 82 | if ($this->getTagsTV()) { 83 | $c->leftJoin(modTemplateVarResource::class,'Tags', [ 84 | 'Tags.tmplvarid' => $this->tvTags->get('id'), 85 | 'Tags.contentid = Article.id', 86 | ]); 87 | } 88 | 89 | $parent = $this->getProperty('parent',null); 90 | if (!empty($parent)) { 91 | $c->where([ 92 | 'parent' => $parent, 93 | ]); 94 | } 95 | $query = $this->getProperty('query',null); 96 | if (!empty($query)) { 97 | $queryWhere = [ 98 | 'pagetitle:LIKE' => '%'.$query.'%', 99 | 'OR:description:LIKE' => '%'.$query.'%', 100 | 'OR:introtext:LIKE' => '%'.$query.'%', 101 | ]; 102 | if ($this->tvTags) { 103 | $queryWhere['OR:Tags.value:LIKE'] = '%'.$query.'%'; 104 | } 105 | $c->where($queryWhere); 106 | } 107 | $filter = $this->getProperty('filter',''); 108 | switch ($filter) { 109 | case 'published': 110 | $c->where([ 111 | 'published' => 1, 112 | 'deleted' => 0, 113 | ]); 114 | break; 115 | case 'unpublished': 116 | $c->where([ 117 | 'published' => 0, 118 | 'deleted' => 0, 119 | ]); 120 | break; 121 | case 'deleted': 122 | $c->where([ 123 | 'deleted' => 1, 124 | ]); 125 | break; 126 | default: 127 | $c->where([ 128 | 'deleted' => 0, 129 | ]); 130 | break; 131 | } 132 | 133 | $c->where([ 134 | 'class_key' => Article::class, 135 | ]); 136 | return $c; 137 | } 138 | 139 | public function getSortClassKey() { 140 | $classKey = Article::class; 141 | switch ($this->getProperty('sort')) { 142 | case 'tags': 143 | $classKey = modTemplateVarResource::class; 144 | break; 145 | } 146 | return $classKey; 147 | } 148 | 149 | public function prepareQueryAfterCount(\xPDO\Om\xPDOQuery $c) { 150 | $c->select($this->modx->getSelectColumns(Article::class,'Article')); 151 | $c->select([ 152 | 'createdby_username' => 'CreatedBy.username', 153 | ]); 154 | if ($this->tvTags) { 155 | $c->select([ 156 | 'tags' => 'Tags.value', 157 | ]); 158 | } 159 | if ($this->commentsEnabled) { 160 | $commentsQuery = $this->modx->newQuery(quipComment::class); 161 | $commentsQuery->innerJoin(quipThread::class,'Thread'); 162 | $commentsQuery->where([ 163 | 'Thread.resource = Article.id', 164 | ]); 165 | $commentsQuery->select([ 166 | 'COUNT('.$this->modx->getSelectColumns(quipComment::class,'quipComment','', ['id']).')', 167 | ]); 168 | $commentsQuery->construct(); 169 | $c->select([ 170 | '('.$commentsQuery->toSQL().') AS '.$this->modx->escape('comments'), 171 | ]); 172 | } 173 | return $c; 174 | } 175 | 176 | /** 177 | * @param \xPDO\Om\xPDOObject|Article $object 178 | * @return array 179 | */ 180 | public function prepareRow(\xPDO\Om\xPDOObject $object) { 181 | $resourceArray = parent::prepareRow($object); 182 | 183 | if (!empty($resourceArray['publishedon'])) { 184 | $publishedon = strtotime($resourceArray['publishedon']); 185 | $resourceArray['publishedon_date'] = strftime($this->modx->getOption('articles.mgr_date_format',null,'%b %d'),$publishedon); 186 | $resourceArray['publishedon_time'] = strftime($this->modx->getOption('articles.mgr_time_format',null,'%H:%I %p'),$publishedon); 187 | $resourceArray['publishedon'] = strftime('%b %d, %Y %H:%I %p',$publishedon); 188 | } 189 | $resourceArray['action_edit'] = '?a='.$this->editAction.'&action=post/update&id='.$resourceArray['id']; 190 | if (!array_key_exists('comments',$resourceArray)) $resourceArray['comments'] = 0; 191 | 192 | $this->modx->getContext($resourceArray['context_key']); 193 | $resourceArray['preview_url'] = $this->modx->makeUrl($resourceArray['id'],$resourceArray['context_key']); 194 | 195 | $trimLength = $this->modx->getOption('articles.mgr_article_content_preview_length',null,300); 196 | $resourceArray['content'] = strip_tags($this->ellipsis($object->getContent(),$trimLength)); 197 | 198 | $resourceArray['actions'] = []; 199 | $resourceArray['actions'][] = [ 200 | 'className' => 'edit', 201 | 'text' => $this->modx->lexicon('edit'), 202 | ]; 203 | $resourceArray['actions'][] = [ 204 | 'className' => 'view', 205 | 'text' => $this->modx->lexicon('view'), 206 | ]; 207 | if (!empty($resourceArray['deleted'])) { 208 | $resourceArray['actions'][] = [ 209 | 'className' => 'undelete', 210 | 'text' => $this->modx->lexicon('undelete'), 211 | ]; 212 | } else { 213 | $resourceArray['actions'][] = [ 214 | 'className' => 'delete', 215 | 'text' => $this->modx->lexicon('delete'), 216 | ]; 217 | } 218 | if (!empty($resourceArray['published'])) { 219 | $resourceArray['actions'][] = [ 220 | 'className' => 'unpublish', 221 | 'text' => $this->modx->lexicon('unpublish'), 222 | ]; 223 | } else { 224 | $resourceArray['actions'][] = [ 225 | 'className' => 'publish orange', 226 | 'text' => $this->modx->lexicon('publish'), 227 | ]; 228 | } 229 | return $resourceArray; 230 | } 231 | 232 | public function ellipsis($string,$length = 300) { 233 | if (mb_strlen($string) > $length) { 234 | $encoding = $this->modx->getOption('modx_charset',null,'UTF-8'); 235 | $string = mb_substr($string,0,$length,$encoding).'...'; 236 | } 237 | return $string; 238 | } 239 | } 240 | return GetList::class; 241 | -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/Ping.php: -------------------------------------------------------------------------------- 1 | getProperty('id',null); 18 | if (empty($id)) { return $this->modx->lexicon('articles.articles_err_ns'); } 19 | $this->object = $this->modx->getObject(Article::class,$id); 20 | if (empty($this->object)) return $this->modx->lexicon('articles.article_err_nf'); 21 | return $initialized; 22 | } 23 | public function process() { 24 | if ($this->object->notifyUpdateServices()) { 25 | return $this->success(); 26 | } else { 27 | return $this->failure(); 28 | } 29 | } 30 | } 31 | return Ping::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/PublishMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids',null); 19 | if (empty($ids)) { 20 | return $this->failure($this->modx->lexicon('articles.articles_err_ns_multiple')); 21 | } 22 | $ids = is_array($ids) ? $ids : explode(',',$ids); 23 | 24 | foreach ($ids as $id) { 25 | if (empty($id)) continue; 26 | $this->modx->runProcessor('resource/publish', [ 27 | 'id' => $id, 28 | ]); 29 | } 30 | return $this->success(); 31 | } 32 | } 33 | return PublishMultiple::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/UnDeleteMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids',null); 19 | if (empty($ids)) { 20 | return $this->failure($this->modx->lexicon('articles.articles_err_ns_multiple')); 21 | } 22 | $ids = is_array($ids) ? $ids : explode(',',$ids); 23 | 24 | foreach ($ids as $id) { 25 | if (empty($id)) continue; 26 | $this->modx->runProcessor('resource/undelete', [ 27 | 'id' => $id, 28 | ]); 29 | } 30 | return $this->success(); 31 | } 32 | } 33 | return UnDeleteMultiple::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Article/UnPublishMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids',null); 19 | if (empty($ids)) { 20 | return $this->failure($this->modx->lexicon('articles.articles_err_ns_multiple')); 21 | } 22 | $ids = is_array($ids) ? $ids : explode(',',$ids); 23 | 24 | foreach ($ids as $id) { 25 | if (empty($id)) continue; 26 | $this->modx->runProcessor('resource/unpublish', [ 27 | 'id' => $id, 28 | ]); 29 | } 30 | return $this->success(); 31 | } 32 | } 33 | return UnPublishMultiple::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Container/Import.php: -------------------------------------------------------------------------------- 1 | getProperty('id',null); 26 | if (empty($id)) { return $this->modx->lexicon('articles.container_err_ns'); } 27 | $this->object = $this->modx->getObject(ArticlesContainer::class,$id); 28 | if (empty($this->object)) return $this->modx->lexicon('articles.container_err_nf'); 29 | return $initialized; 30 | } 31 | 32 | /** 33 | * Import data into Articles 34 | * {@inheritDoc} 35 | * @return array|string 36 | */ 37 | public function process() { 38 | $this->getImportService(); 39 | if (empty($this->service)) { 40 | return $this->failure('[Articles] Could not load import service!'); 41 | } 42 | 43 | $success = $this->service->import(); 44 | 45 | if ($success) { 46 | $this->clearCache(); 47 | return $this->success(); 48 | } else { 49 | return $this->failure(); 50 | } 51 | } 52 | 53 | /** 54 | * Get the specified import service 55 | * @return ArticlesImport 56 | */ 57 | public function getImportService() { 58 | $serviceName = $this->getProperty('service','WordPress'); 59 | 60 | $modelPath = $this->modx->getOption('articles.core_path',null,$this->modx->getOption('core_path').'components/articles/') . 'src/Model/'; 61 | $servicePath = $modelPath . 'Import/ArticlesImport' . ucfirst(strtolower($serviceName)) . '.php'; 62 | if (file_exists($servicePath)) { 63 | require_once $servicePath; 64 | $className = ArticlesImport::class.$serviceName; 65 | $this->service = new $className(new \Articles\Articles($this->modx),$this,$this->getProperties()); 66 | } 67 | 68 | return $this->service; 69 | } 70 | 71 | /** 72 | * Clear the site cache to properly refresh the URIs 73 | */ 74 | public function clearCache() { 75 | $this->modx->cacheManager->refresh([ 76 | 'db' => [], 77 | 'auto_publish' => ['contexts' => [$this->object->get('context_key')]], 78 | 'context_settings' => ['contexts' => [$this->object->get('context_key')]], 79 | 'resource' => ['contexts' => [$this->object->get('context_key')]], 80 | ]); 81 | } 82 | } 83 | return Import::class; -------------------------------------------------------------------------------- /core/components/articles/src/Processors/Extras/GetTags.php: -------------------------------------------------------------------------------- 1 | getProperty('container', false); 21 | if(!$container){ 22 | return false; 23 | } 24 | 25 | $parent = $this->modx->getObject(modResource::class, $container); 26 | if(!$parent){ 27 | return false; 28 | } 29 | 30 | $articles = $parent->getMany('Children', ['deleted' => 0]); 31 | $articleIDs = []; 32 | foreach($articles as $article){ 33 | $articleIDs[] = $article->id; 34 | } 35 | 36 | $templateVariable = $this->modx->getObject(modTemplateVar::class, ['name' => 'articlestags']); 37 | if(!$templateVariable){ 38 | return false; 39 | } 40 | 41 | $c = $this->modx->newQuery(modTemplateVarResource::class); 42 | 43 | $c->where([ 44 | 'tmplvarid' => $templateVariable->id, 45 | 'contentid:IN' => $articleIDs 46 | ]); 47 | 48 | $tagsObject = $this->modx->getCollection(modTemplateVarResource::class, $c); 49 | $tags = []; 50 | 51 | foreach($tagsObject as $tagObject){ 52 | $addTags = explode(',',$tagObject->value); 53 | foreach($addTags as &$addTag){ 54 | $addTag = trim($addTag); 55 | } 56 | $tags = array_merge($tags, $addTags); 57 | } 58 | 59 | $tags = Articles::arrayUnique($tags); 60 | sort($tags); 61 | $returnArray = []; 62 | foreach($tags as $tag){ 63 | $returnArray[] = [$tag]; 64 | } 65 | 66 | return $this->success('', $returnArray); 67 | } 68 | 69 | } 70 | return GetTags::class; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Articles 2 | 3 | This is the official repo for the MODX blogging extra Articles. 4 | 5 | ## Introduction 6 | 7 | Articles is a MODX Extra that incorporates a combo of other packages geared towards blog/article writing in MODX. It manages your posts and comments in custom grids, handles archiving and tagging all within a unified interface. 8 | 9 | Articles is made up of the following packages: 10 | 11 | - Quip 12 | - TagLister 13 | - Archivist 14 | - getResources 15 | - getPage 16 | 17 | When installing Articles, the package manager will attempt to also download and install these for you if not already present. 18 | 19 | ## MODX 3.x Compatibility - Alpha Version 20 | 21 | At the time of writing, MODX 3.0.0-pl has just been released and there has been a lot of refactoring work done to make Articles compatible. However, due to incompatible class keys and the way custom resources classes function (Articles makes use of these) in MODX, the developers have had to split Articles into two versions. 22 | 23 | Before upgrading to MODX 3 with Articles installed, you'll need to update Articles to at least version 1.8.0-pl. This will ensure an older incompatible version of Articles doesn't cause any errors during the upgrade process. 24 | 25 | Once upgraded to MODX 3.0, articles won't yet be fully usable. Go to the package manager, and you'll see a new version of Articles (2.0.0-alpha1 at time of writing) will be available. Download and install this to start using Articles with MODX 3! 26 | 27 | Please bear in mind that it's currently an Alpha version, and be sure to report any issues you encounter on Github: [https://github.com/modxcms/Articles](https://github.com/modxcms/Articles) 28 | 29 | * The documentation can be found at [MODX Docs](https://docs.modx.org/current/en/extras/articles). 30 | * Help can be found at the [MODX Community](https://community.modx.com/c/support/extras) 31 | 32 | ## Slack 33 | Join the conversation in our [public Slack workspace](https://modx.org). 34 | --------------------------------------------------------------------------------