Файл: concrete5.7.5.6/concrete/src/Package/Package.php
Строк: 1356
<?php
namespace ConcreteCorePackage;
use BlockType;
use BlockTypeList;
use ConcreteCoreAntispamLibrary as SystemAntispamLibrary;
use ConcreteCoreAttributeKeyCategory as AttributeKeyCategory;
use ConcreteCoreAttributeKeyKey as AttributeKey;
use ConcreteCoreAttributeSet as AttributeSet;
use ConcreteCoreAttributeType as AttributeType;
use ConcreteCoreAuthenticationAuthenticationType as AuthenticationType;
use ConcreteCoreBackupContentImporter;
use ConcreteCoreBlockBlockTypeSet as BlockTypeSet;
use ConcreteCoreCaptchaLibrary as SystemCaptchaLibrary;
use ConcreteCoreConfigRepositoryLiaison;
use ConcreteCoreConversationEditorEditor as ConversationEditor;
use ConcreteCoreConversationRatingType as ConversationRatingType;
use ConcreteCoreDatabaseEntityManagerFactory;
use ConcreteCoreDatabaseEntityManagerFactoryInterface;
use ConcreteCoreDatabaseSchemaSchema;
use ConcreteCoreEditorSnippet as SystemContentEditorSnippet;
use ConcreteCoreFeatureCategoryCategory as FeatureCategory;
use ConcreteCoreFeatureFeature;
use ConcreteCoreFileFileList;
use ConcreteCoreFileStorageLocationTypeType as StorageLocation;
use ConcreteCoreFoundationClassLoader;
use ConcreteCoreFoundationObject;
use ConcreteCoreGatheringDataSourceDataSource as GatheringDataSource;
use ConcreteCoreGatheringItemTemplateTemplate as GatheringItemTemplate;
use ConcreteCoreGatheringItemTemplateType as GatheringItemTemplateType;
use ConcreteCoreMailImporterMailImporter;
use ConcreteCorePagePageList;
use ConcreteCorePageStackStackList;
use ConcreteCorePageTypeComposerControlTypeType as PageTypeComposerControlType;
use ConcreteCorePageTypePublishTargetTypeType as PageTypePublishTargetType;
use ConcreteCorePermissionAccessEntityType as PermissionAccessEntityType;
use ConcreteCoreUserPointActionAction as UserPointAction;
use ConcreteCoreWorkflowProgressCategory as WorkflowProgressCategory;
use ConcreteCoreWorkflowType as WorkflowType;
use Core;
use Database;
use Environment;
use GroupSet;
use Job;
use Localization;
use ORM;
use Page;
use PageTemplate;
use PageTheme;
use PageType;
use PermissionKey;
use PermissionKeyCategory;
use SinglePage;
/**
* A package can contains related components that customize concrete5. They can br easily
* installed and uninstall by a user.
*
* @property string $pkgName Installed name of package
* @property string $pkgHandle Installed handle of package. This should be provided by the ending package.
* @property string $pkgDescription Installed description of package
* @property bool $pkgIsInstalled True if package is installed
* @property string $pkgVersion Version of package installed
* @property string $pkgAvailableVersion
*/
class Package extends Object
{
const E_PACKAGE_NOT_FOUND = 1;
const E_PACKAGE_INSTALLED = 2;
const E_PACKAGE_VERSION = 3;
const E_PACKAGE_DOWNLOAD = 4;
const E_PACKAGE_SAVE = 5;
const E_PACKAGE_UNZIP = 6;
const E_PACKAGE_INSTALL = 7;
const E_PACKAGE_MIGRATE_BACKUP = 8;
const E_PACKAGE_INVALID_APP_VERSION = 20;
const E_PACKAGE_THEME_ACTIVE = 21;
protected $DIR_PACKAGES_CORE = DIR_PACKAGES_CORE;
protected $DIR_PACKAGES = DIR_PACKAGES;
protected $REL_DIR_PACKAGES_CORE = REL_DIR_PACKAGES_CORE;
protected $REL_DIR_PACKAGES = REL_DIR_PACKAGES;
protected $backedUpFname = '';
/**
* The package ID.
* Don't access this directly: use Package->getPackageID and Package->setPackageID.
*
* @var int|null
*
* @internal
*/
public $pkgID = null;
/**
* @var ConcreteCoreConfigRepositoryLiaison
*/
protected $config;
/**
* @var ConcreteCoreConfigRepositoryLiaison
*/
protected $fileConfig;
/**
* @var ConcreteCoreDatabaseDatabaseStructureManager
*/
protected $databaseStructureManager;
protected $appVersionRequired = '5.7.0';
protected $pkgAllowsFullContentSwap = false;
protected $pkgContentProvidesFileThumbnails = false;
protected $pkgAutoloaderMapCoreExtensions = false;
protected $pkgAutoloaderRegistries = array();
protected $errorText = array();
/** Returns the display name of a category of package items (localized and escaped accordingly to $format)
* @param string $categoryHandle The category handle
* @param string $format = 'html' Escape the result in html format (if $format is 'html'). If $format is 'text' or any other value, the display name won't be escaped.
*
* @return string
*/
public static function getPackageItemsCategoryDisplayName($categoryHandle, $format = 'html')
{
switch ($categoryHandle) {
case 'attribute_categories':
$value = t('Attribute categories');
break;
case 'permission_categories':
$value = t('Permission categories');
break;
case 'permission_access_entity_types':
$value = t('Permission access entity types');
break;
case 'attribute_keys':
$value = t('Attribute keys');
break;
case 'attribute_sets':
$value = t('Attribute sets');
break;
case 'group_sets':
$value = t('Group sets');
break;
case 'page_types':
$value = t('Page types');
break;
case 'mail_importers':
$value = t('Mail importers');
break;
case 'block_types':
$value = t('Block types');
break;
case 'page_themes':
$value = t('Page themes');
break;
case 'permissions':
$value = t('Permissions');
break;
case 'single_pages':
$value = t('Single pages');
break;
case 'attribute_types':
$value = t('Attribute types');
break;
case 'captcha_libraries':
$value = t('Captcha libraries');
break;
case 'antispam_libraries':
$value = t('Antispam libraries');
break;
case 'jobs':
$value = t('Jobs');
break;
case 'workflow_types':
$value = t('Workflow types');
break;
case 'workflow_progress_categories':
$value = t('Workflow progress categories');
break;
case 'storage_locations':
$value = t('Storage Locations');
break;
default:
$value = t(Core::make('helper/text')->unhandle($categoryHandle));
break;
}
switch ($format) {
case 'html':
return h($value);
case 'text':
default:
return $value;
}
}
/**
* Returns the name of an object belonging to a package.
*
* @param mixed $item
*
* @return string
*/
public static function getItemName($item)
{
$txt = Core::make('helper/text');
if ($item instanceof BlockType) {
return t($item->getBlockTypeName());
} elseif ($item instanceof PageTheme) {
return $item->getThemeDisplayName();
} elseif ($item instanceof Feature) {
return $item->getFeatureName();
} elseif ($item instanceof FeatureCategory) {
return $item->getFeatureCategoryName();
} elseif ($item instanceof GatheringDataSource) {
return $item->getGatheringDataSourceName();
} elseif ($item instanceof GatheringItemTemplateType) {
return $txt->unhandle($item->getGatheringItemTemplateTypeHandle());
} elseif ($item instanceof GatheringItemTemplate) {
return $item->getGatheringItemTemplateName();
} elseif ($item instanceof BlockTypeSet) {
return $item->getBlockTypeSetDisplayName();
} elseif ($item instanceof PageTypeComposerControlType) {
return $item->getPageTypeComposerControlTypeDisplayName();
} elseif ($item instanceof PageTypePublishTargetType) {
return $item->getPageTypePublishTargetTypeDisplayName();
} elseif ($item instanceof PageType) {
return $item->getPageTypeName();
} elseif ($item instanceof PageTemplate) {
return $item->getPageTemplateDisplayName();
} elseif ($item instanceof MailImporter) {
return $item->getMailImporterName();
} elseif ($item instanceof Page) {
return $item->getCollectionPath();
} elseif ($item instanceof AttributeType) {
return $item->getAttributeTypeDisplayName();
} elseif ($item instanceof PermissionAccessEntityType) {
return $item->getAccessEntityTypeDisplayName();
} elseif ($item instanceof PermissionKeyCategory) {
return $txt->unhandle($item->getPermissionKeyCategoryHandle());
} elseif ($item instanceof WorkflowProgressCategory) {
return $txt->unhandle($item->getWorkflowProgressCategoryHandle());
} elseif ($item instanceof AttributeKeyCategory) {
return $txt->unhandle($item->getAttributeKeyCategoryHandle());
} elseif ($item instanceof AttributeSet) {
$at = AttributeKeyCategory::getByID($item->getAttributeSetKeyCategoryID());
return t(
'%s (%s)',
$item->getAttributeSetDisplayName(),
$txt->unhandle($at->getAttributeKeyCategoryHandle()));
} elseif ($item instanceof GroupSet) {
return $item->getGroupSetDisplayName();
} elseif ($item instanceof AttributeKey) {
$akc = AttributeKeyCategory::getByID($item->getAttributeKeyCategoryID());
return t(
' %s (%s)',
$txt->unhandle($item->getAttributeKeyHandle()),
$txt->unhandle($akc->getAttributeKeyCategoryHandle()));
} elseif ($item instanceof UserPointAction) {
return $item->getUserPointActionName();
} elseif ($item instanceof SystemCaptchaLibrary) {
return $item->getSystemCaptchaLibraryName();
} elseif ($item instanceof SystemAntispamLibrary) {
return $item->getSystemAntispamLibraryName();
} elseif ($item instanceof ConversationRatingType) {
return $item->getConversationRatingTypeDisplayName();
} elseif ($item instanceof SystemContentEditorSnippet) {
return $item->getSystemContentEditorSnippetName();
} elseif ($item instanceof AuthenticationType) {
return $item->getAuthenticationTypeName();
} elseif (is_a($item, 'PermissionKey')) {
return $item->getPermissionKeyDisplayName();
} elseif (is_a($item, 'Job')) {
return $item->getJobName();
} elseif (is_a($item, 'WorkflowType')) {
return $item->getWorkflowTypeName();
} elseif ($item instanceof StorageLocation) {
return $item->getName();
}
}
/**
* This is the pre-test routine that packages run through before they are installed. Any errors that come here are
* to be returned in the form of an array so we can show the user. If it's all good we return true.
*
* @param string $package Package handle
* @param bool $testForAlreadyInstalled
*
* @return array|bool Returns an array of errors or true if the package can be installed
*/
public static function testForInstall($package, $testForAlreadyInstalled = true)
{
// this is the pre-test routine that packages run through before they are installed. Any errors that come here
// are to be returned in the form of an array so we can show the user. If it's all good we return true
$db = Database::connection();
$errors = array();
$pkg = static::getClass($package);
// Step 1 does that package exist ?
if ((!is_dir(DIR_PACKAGES . '/' . $package) && (!is_dir(
DIR_PACKAGES_CORE . '/' . $package))) || $package == ''
) {
$errors[] = self::E_PACKAGE_NOT_FOUND;
} elseif ($pkg instanceof BrokenPackage) {
$errors[] = self::E_PACKAGE_NOT_FOUND;
}
// Step 2 - check to see if the user has already installed a package w/this handle
if ($testForAlreadyInstalled) {
$cnt = $db->fetchColumn("SELECT count(*) FROM Packages WHERE pkgHandle = ?", array($package));
if ($cnt > 0) {
$errors[] = self::E_PACKAGE_INSTALLED;
}
}
if (count($errors) == 0) {
// test minimum application version requirement
if (version_compare(APP_VERSION, $pkg->getApplicationVersionRequired(), '<')) {
$errors[] = array(self::E_PACKAGE_VERSION, $pkg->getApplicationVersionRequired());
}
}
if (count($errors) > 0) {
return $errors;
} else {
return true;
}
}
/**
* Returns a package's class.
*
* @param string $pkgHandle Handle of package
*
* @return Package
*/
public static function getClass($pkgHandle)
{
$cache = Core::make('cache/request');
$item = $cache->getItem('package/class/' . $pkgHandle);
$cl = $item->get();
if ($item->isMiss()) {
$item->lock();
// loads and instantiates the object
$cl = ConcreteCoreFoundationClassLoader::getInstance();
$cl->registerPackageController($pkgHandle);
$class = '\Concrete\Package\' . camelcase($pkgHandle) . '\Controller';
try {
$cl = Core::make($class);
} catch (Exception $ex) {
$cl = new BrokenPackage($pkgHandle);
}
$item->set($cl);
}
return clone $cl;
}
/**
* Returns the version of concrete5 required by the package.
*
* @return string
*/
public function getApplicationVersionRequired()
{
return $this->appVersionRequired;
}
/**
* Returns a Package object for the given package handle, null if not found.
*
* @param string $pkgHandle
*
* @return Package
*/
public static function getByHandle($pkgHandle)
{
$db = Database::connection();
$row = $db->fetchAssoc("SELECT * FROM Packages WHERE pkgHandle = ?", array($pkgHandle));
if ($row) {
$pkg = static::getClass($row['pkgHandle']);
if ($pkg instanceof self) {
$pkg->setPropertiesFromArray($row);
}
return $pkg;
} else {
return null;
}
}
/**
* Returns an array of packages that have newer versions in the local packages directory
* than those which are in the Packages table. This means they're ready to be upgraded.
*
* @return Package[]
*/
public static function getLocalUpgradeablePackages()
{
$packages = self::getAvailablePackages(false);
$upgradeables = array();
$db = Database::connection();
foreach ($packages as $p) {
$row = $db->fetchAssoc(
"SELECT pkgID, pkgVersion FROM Packages WHERE pkgHandle = ? AND pkgIsInstalled = 1",
array($p->getPackageHandle())
);
if ($row) {
if (version_compare($p->getPackageVersion(), $row['pkgVersion'], '>')) {
$p->pkgCurrentVersion = $row['pkgVersion'];
$upgradeables[] = $p;
}
}
}
return $upgradeables;
}
/**
* Returns all available packages.
*
* @param bool $filterInstalled True to only return installed packages
*
* @return Package[]
*/
public static function getAvailablePackages($filterInstalled = true)
{
$dh = Core::make('helper/file');
$packages = $dh->getDirectoryContents(DIR_PACKAGES);
if ($filterInstalled) {
$handles = self::getInstalledHandles();
// strip out packages we've already installed
$packagesTemp = array();
foreach ($packages as $p) {
if (!in_array($p, $handles)) {
$packagesTemp[] = $p;
}
}
$packages = $packagesTemp;
}
if (count($packages) > 0) {
$packagesTemp = array();
// get package objects from the file system
foreach ($packages as $p) {
if (file_exists(DIR_PACKAGES . '/' . $p . '/' . FILENAME_CONTROLLER)) {
$pkg = static::getClass($p);
if (!empty($pkg)) {
$packagesTemp[] = $pkg;
}
}
}
$packages = $packagesTemp;
}
return $packages;
}
/**
* Returns all installed package handles.
*
* @return string[]
*/
public static function getInstalledHandles()
{
$db = Database::connection();
return $db->GetCol("SELECT pkgHandle FROM Packages");
}
/**
* Finds all packages that have an upgraded version available in the marketplace.
*
* @return Package[]
*/
public static function getRemotelyUpgradeablePackages()
{
$packages = self::getInstalledList();
$upgradeables = array();
foreach ($packages as $p) {
if (version_compare($p->getPackageVersion(), $p->getPackageVersionUpdateAvailable(), '<')) {
$upgradeables[] = $p;
}
}
return $upgradeables;
}
/**
* Returns an array of all installed packages.
*
* @return Package[]
*/
public static function getInstalledList()
{
$db = Database::connection();
$r = $db->query("SELECT * FROM Packages WHERE pkgIsInstalled = 1 ORDER BY pkgDateInstalled ASC");
$pkgArray = array();
while ($row = $r->fetchRow()) {
$pkg = new self();
$pkg->setPropertiesFromArray($row);
$pkgArray[] = $pkg;
}
return $pkgArray;
}
/**
* Returns the path to the package's folder, relative to the install path.
*
* @return string
*/
public function getRelativePath()
{
$dirp = (is_dir(
$this->DIR_PACKAGES . '/' . $this->getPackageHandle())) ? $this->REL_DIR_PACKAGES : $this->REL_DIR_PACKAGES_CORE;
return $dirp . '/' . $this->pkgHandle;
}
/**
* Returns the package handle.
*
* @return string
*/
public function getPackageHandle()
{
return $this->pkgHandle;
}
/**
* Gets the date the package was added to the system.
*
* @return string date formatted like: 2009-01-01 00:00:00
*/
public function getPackageDateInstalled()
{
return $this->pkgDateInstalled;
}
public function getPackageVersionUpdateAvailable()
{
return $this->pkgAvailableVersion;
}
/**
* Returns true if the package is installed, false if not.
*
* @return bool
*/
public function isPackageInstalled()
{
return $this->pkgIsInstalled;
}
/**
* Gets the contents of the package's CHANGELOG file. If no changelog is available an empty string is returned.
*
* @return string
*/
public function getChangelogContents()
{
if (file_exists($this->getPackagePath() . '/CHANGELOG')) {
$contents = Core::make('helper/file')->getContents($this->getPackagePath() . '/CHANGELOG');
return nl2br(Core::make('helper/text')->entities($contents));
}
return '';
}
public function getPackagePath()
{
$dirp = (is_dir(
$this->DIR_PACKAGES . '/' . $this->getPackageHandle())) ? $this->DIR_PACKAGES : $this->DIR_PACKAGES_CORE;
$path = $dirp . '/' . $this->getPackageHandle();
return $path;
}
/**
* Returns the currently installed package version.
* NOTE: This function only returns a value if getLocalUpgradeablePackages() has been called first!
*
* @return string
*/
public function getPackageCurrentlyInstalledVersion()
{
return $this->pkgCurrentVersion;
}
/**
* @return bool
*/
public function providesCoreExtensionAutoloaderMapping()
{
return $this->pkgAutoloaderMapCoreExtensions;
}
/**
* Returns custom autoloader prefixes registered by the class loader.
*
* @return array Keys represent the namespace, not relative to the package's namespace. Values are the path, and are relative to the package directory.
*/
public function getPackageAutoloaderRegistries()
{
return $this->pkgAutoloaderRegistries;
}
/**
* Returns true if the package has a post install screen.
*
* @return bool
*/
public function hasInstallPostScreen()
{
return file_exists(
$this->getPackagePath() . '/' . DIRNAME_ELEMENTS . '/' . DIRNAME_DASHBOARD . '/install_post.php');
}
/**
* Returns true if the package has an install options screen.
*
* @return bool
*/
public function showInstallOptionsScreen()
{
return $this->hasInstallNotes() || $this->allowsFullContentSwap();
}
public function hasInstallNotes()
{
return file_exists($this->getPackagePath() . '/' . DIRNAME_ELEMENTS . '/' . DIRNAME_DASHBOARD . '/install.php');
}
public function hasUninstallNotes()
{
return file_exists($this->getPackagePath() . '/' . DIRNAME_ELEMENTS . '/' . DIRNAME_DASHBOARD . '/uninstall.php');
}
public function allowsFullContentSwap()
{
return $this->pkgAllowsFullContentSwap;
}
/**
* Loads package translation files into zend translate.
*
* @param string $locale = null The identifier of the locale to activate (used to build the language file path). If empty we'll use currently active locale.
* @param ZendI18nTranslatorTranslator|string $translate = 'current' The Zend Translator instance that holds the translations (set to 'current' to use the current one)
*/
public function setupPackageLocalization($locale = null, $translate = 'current')
{
if ($translate === 'current') {
$translate = Localization::getTranslate();
}
if (is_object($translate)) {
$path = $this->getPackagePath() . '/' . DIRNAME_LANGUAGES;
if (!isset($locale) || !strlen($locale)) {
$locale = Localization::activeLocale();
}
$languageFile = "$path/$locale/LC_MESSAGES/messages.mo";
if (is_file($languageFile)) {
$translate->addTranslationFile('gettext', $languageFile);
}
}
}
/**
* @return bool|int[] true on success, array of error codes on failure
*/
public function testForUninstall()
{
$errors = array();
$items = $this->getPackageItems();
/** @var PageTheme[] $themes */
$themes = array_get($items, 'page_themes', array());
// Step 1, check for active themes
$active_theme = PageTheme::getSiteTheme();
foreach ($themes as $theme) {
if ($active_theme->getThemeID() == $theme->getThemeID()) {
$errors[] = self::E_PACKAGE_THEME_ACTIVE;
break;
}
}
return count($errors) ? $errors : true;
}
/**
* Uninstalls the package. Removes any blocks, themes, or pages associated with the package.
*/
public function uninstall()
{
$db = Database::connection();
$items = $this->getPackageItems();
foreach ($items as $k => $array) {
if (!is_array($array)) {
continue;
}
foreach ($array as $item) {
if ($item instanceof AuthenticationType) {
$item->delete();
}
if (is_a($item, 'Job')) {
$item->uninstall();
} elseif (is_a($item, 'AttributeKey') || is_a($item, 'MailImporter')) {
$item->delete();
} else {
switch (get_class($item)) {
case 'BlockType':
case 'GatheringDataSource':
case 'BlockTypeSet':
case 'Feature':
case 'FeatureCategory':
case 'GatheringItemTemplate':
case 'ConversationEditor':
case 'ConversationRatingType':
case 'PageTypePublishTargetType':
case 'PageTypeComposerControlType':
case 'PageTemplate':
$item->delete();
break;
case 'PageTheme':
$item->uninstall();
break;
case 'Page':
@$item->delete(); // we suppress errors because sometimes the wrapper pages can delete first.
break;
case 'SystemCaptchaLibrary':
case 'SystemContentEditorSnippet':
case 'SystemAntispamLibrary':
$item->delete();
break;
case 'PageType':
$item->delete();
break;
case 'MailImporter':
$item->delete();
break;
case 'UserPointAction':
case 'AttributeKeyCategory':
case 'PermissionKeyCategory':
case 'AttributeSet':
case 'GroupSet':
case 'AttributeType':
case 'WorkflowType':
case 'WorkflowProgressCategory':
case 'PermissionKey':
case 'PermissionAccessEntityType':
$item->delete();
break;
default:
if (method_exists($item, 'delete')) {
$item->delete();
} elseif (method_exists($item, 'uninstall')) {
$item->uninstall();
}
break;
}
}
}
}
Config::clearNamespace($this->getPackageHandle());
Core::make('config/database')->clearNamespace($this->getPackageHandle());
$this->destroyProxyClasses();
$db->executeQuery("DELETE FROM Packages WHERE pkgID = ?", array($this->pkgID));
Localization::clearCache();
}
/**
* Returns an array of package items (e.g. blocks, themes).
*
* @return array
*/
public function getPackageItems()
{
$items = array();
$items['attribute_categories'] = AttributeKeyCategory::getListByPackage($this);
$items['permission_categories'] = PermissionKeyCategory::getListByPackage($this);
$items['permission_access_entity_types'] = PermissionAccessEntityType::getListByPackage($this);
$items['attribute_keys'] = AttributeKey::getListByPackage($this);
$items['attribute_sets'] = AttributeSet::getListByPackage($this);
$items['group_sets'] = GroupSet::getListByPackage($this);
$items['page_types'] = PageType::getListByPackage($this);
$items['page_templates'] = PageTemplate::getListByPackage($this);
$items['mail_importers'] = MailImporter::getListByPackage($this);
$items['gathering_item_template_types'] = GatheringItemTemplateType::getListByPackage($this);
$items['gathering_item_templates'] = GatheringItemTemplate::getListByPackage($this);
$items['gathering_data_sources'] = GatheringDataSource::getListByPackage($this);
$items['features'] = Feature::getListByPackage($this);
$items['feature_categories'] = FeatureCategory::getListByPackage($this);
$btl = new BlockTypeList();
$btl->filterByPackage($this);
$blocktypes = $btl->get();
$items['block_types'] = $blocktypes;
$items['block_type_sets'] = BlockTypeSet::getListByPackage($this);
$items['page_themes'] = PageTheme::getListByPackage($this);
$items['permissions'] = PermissionKey::getListByPackage($this);
$items['single_pages'] = SinglePage::getListByPackage($this);
$items['attribute_types'] = AttributeType::getListByPackage($this);
$items['captcha_libraries'] = SystemCaptchaLibrary::getListByPackage($this);
$items['content_editor_snippets'] = SystemContentEditorSnippet::getListByPackage($this);
$items['conversation_editors'] = ConversationEditor::getListByPackage($this);
$items['conversation_rating_types'] = ConversationRatingType::getListByPackage($this);
$items['page_type_publish_target_types'] = PageTypePublishTargetType::getListByPackage($this);
$items['page_type_composer_control_types'] = PageTypeComposerControlType::getListByPackage($this);
$items['antispam_libraries'] = SystemAntispamLibrary::getListByPackage($this);
$items['community_point_actions'] = UserPointAction::getListByPackage($this);
$items['jobs'] = Job::getListByPackage($this);
$items['workflow_types'] = WorkflowType::getListByPackage($this);
$items['workflow_progress_categories'] = WorkflowProgressCategory::getListByPackage($this);
$items['authentication_types'] = AuthenticationType::getListByPackage($this);
$items['storage_locations'] = StorageLocation::getListByPackage($this);
ksort($items);
return $items;
}
/**
* Destroys all the existing proxy classes for this package.
*
* @return bool
*/
protected function destroyProxyClasses()
{
$dbm = $this->getDatabaseStructureManager();
$config = $dbm->getEntityManager()->getConfiguration();
if (is_object($cache = $config->getMetadataCacheImpl())) {
$cache->flushAll();
}
return $dbm->destroyProxyClasses('ConcretePackage' . camelcase($this->getPackageHandle()) . 'Src');
}
/**
* Gets a package specific entity manager.
*
* @return ConcreteCoreDatabaseDatabaseStructureManager
*/
public function getDatabaseStructureManager()
{
if (!isset($this->databaseStructureManager)) {
$this->databaseStructureManager = Core::make('database/structure', array($this->getEntityManager()));
}
return $this->databaseStructureManager;
}
/**
* @return EntityManagerFactoryInterface
*/
public function getEntityManagerFactory()
{
return new EntityManagerFactory($this->getPackageEntitiesPath());
}
/**
* Gets a package specific entity manager.
*
* @return DoctrineORMEntityManager
*/
public function getEntityManager()
{
return ORM::entityManager($this);
}
/**
* Removes any existing pages, files, stacks, block and page types and installs content from the package.
*
* @param $options
*/
public function swapContent($options)
{
if ($this->validateClearSiteContents($options)) {
Core::make('cache/request')->disable();
$pl = new PageList();
$pages = $pl->getResults();
foreach ($pages as $c) {
$c->delete();
}
$fl = new FileList();
$files = $fl->getResults();
foreach ($files as $f) {
$f->delete();
}
// clear stacks
$sl = new StackList();
foreach ($sl->get() as $c) {
$c->delete();
}
$home = Page::getByID(HOME_CID);
$blocks = $home->getBlocks();
foreach ($blocks as $b) {
$b->deleteBlock();
}
$pageTypes = PageType::getList();
foreach ($pageTypes as $ct) {
$ct->delete();
}
// now we add in any files that this package has
if (is_dir($this->getPackagePath() . '/content_files')) {
$ch = new ContentImporter();
$computeThumbnails = true;
if ($this->contentProvidesFileThumbnails()) {
$computeThumbnails = false;
}
$ch->importFiles($this->getPackagePath() . '/content_files', $computeThumbnails);
}
// now we parse the content.xml if it exists.
$ci = new ContentImporter();
$ci->importContentFile($this->getPackagePath() . '/content.xml');
Core::make('cache/request')->enable();
}
}
/**
* @param array $options
*
* @return bool
*/
protected function validateClearSiteContents($options)
{
if (Core::make('app')->isRunThroughCommandLineInterface()) {
$result = true;
} else {
$result = false;
$u = new User();
if ($u->isSuperUser()) {
// this can ONLY be used through the post. We will use the token to ensure that
$valt = Core::make('helper/validation/token');
if ($valt->validate('install_options_selected', $options['ccm_token'])) {
$result = true;
}
}
}
return $result;
}
/**
* Returns a path to where the packages files are located.
*
* @return string $path
*/
public function contentProvidesFileThumbnails()
{
return $this->pkgContentProvidesFileThumbnails;
}
/**
* Converts package install test errors to human-readable strings.
*
* @param $testResults Package install test errors
*
* @return array
*/
public static function mapError($testResults)
{
$errorText = array(
self::E_PACKAGE_INSTALLED => t("You've already installed that package."),
self::E_PACKAGE_NOT_FOUND => t("Invalid Package."),
self::E_PACKAGE_VERSION => t("This package requires concrete5 version %s or greater."),
self::E_PACKAGE_DOWNLOAD => t("An error occurred while downloading the package."),
self::E_PACKAGE_SAVE => t("concrete5 was not able to save the package after download."),
self::E_PACKAGE_UNZIP => t('An error occurred while trying to unzip the package.'),
self::E_PACKAGE_INSTALL => t('An error occurred while trying to install the package.'),
self::E_PACKAGE_MIGRATE_BACKUP => t(
'Unable to backup old package directory to %s',
Config::get('concrete.misc.package_backup_directory')
),
self::E_PACKAGE_INVALID_APP_VERSION => t(
'This package isn't currently available for this version of concrete5. Please contact the maintainer of this package for assistance.'
),
self::E_PACKAGE_THEME_ACTIVE => t('This package contains the active site theme, please change the theme before uninstalling.'),
);
$testResultsText = array();
foreach ($testResults as $result) {
if (is_array($result)) {
$et = $errorText[$result[0]];
array_shift($result);
$testResultsText[] = vsprintf($et, $result);
} elseif (is_int($result)) {
$testResultsText[] = $errorText[$result];
} elseif (!empty($result)) {
$testResultsText[] = $result;
}
}
return $testResultsText;
}
/**
* Returns the directory containing package entities.
*
* @return string
*/
public function getPackageEntitiesPath()
{
return $this->getPackagePath() . '/' . DIRNAME_CLASSES;
}
/**
* Called to enable package specific configuration.
*/
public function registerConfigNamespace()
{
Config::package($this->getPackageHandle(), $this->getPackagePath() . '/config');
}
/**
* Get the standard database config liaison.
*
* @return ConcreteCoreConfigRepositoryLiaison
*/
public function getConfig()
{
return $this->getDatabaseConfig();
}
/**
* Get the standard database config liaison.
*
* @return ConcreteCoreConfigRepositoryLiaison
*/
public function getDatabaseConfig()
{
if (!$this->config) {
$this->config = new Liaison(Core::make('config/database'), $this->getPackageHandle());
}
return $this->config;
}
/**
* Get the standard filesystem config liaison.
*
* @return ConcreteCoreConfigRepositoryLiaison
*/
public function getFileConfig()
{
if (!$this->fileConfig) {
$this->fileConfig = new Liaison(Core::make('config'), $this->getPackageHandle());
}
return $this->fileConfig;
}
/**
* Installs the package info row and installs the database. Packages installing additional content should override this method, call the parent method,
* and use the resulting package object for further installs.
*
* @return Package
*/
public function install()
{
PackageList::refreshCache();
$db = Database::connection();
$dh = Core::make('helper/date');
$v = array(
$this->getPackageName(),
$this->getPackageDescription(),
$this->getPackageVersion(),
$this->getPackageHandle(),
1,
$dh->getOverridableNow(),
);
$db->query(
"INSERT INTO Packages (pkgName, pkgDescription, pkgVersion, pkgHandle, pkgIsInstalled, pkgDateInstalled) VALUES (?, ?, ?, ?, ?, ?)",
$v
);
$pkg = self::getByID($db->lastInsertId());
ClassLoader::getInstance()->registerPackage($pkg);
$pkg->installDatabase();
$env = Environment::get();
$env->clearOverrideCache();
Localization::clearCache();
return $pkg;
}
/**
* Returns the translated name of the package.
*
* @return string
*/
public function getPackageName()
{
return t($this->pkgName);
}
/**
* Returns the translated package description.
*
* @return string
*/
public function getPackageDescription()
{
return t($this->pkgDescription);
}
/**
* Returns the installed package version.
*
* @return string
*/
public function getPackageVersion()
{
return $this->pkgVersion;
}
/**
* Returns a Package object for the given package id, null if not found.
*
* @param int $pkgID
*
* @return Package
*/
public static function getByID($pkgID)
{
$db = Database::connection();
$row = $db->fetchAssoc("SELECT * FROM Packages WHERE pkgID = ?", array($pkgID));
if ($row) {
$pkg = static::getClass($row['pkgHandle']);
if ($pkg instanceof self) {
$pkg->setPropertiesFromArray($row);
return $pkg;
}
}
return null;
}
/**
* Installs the packages database through doctrine entities and db.xml
* database definitions.
*/
public function installDatabase()
{
$this->installEntitiesDatabase();
static::installDB($this->getPackagePath() . '/' . FILENAME_PACKAGE_DB);
}
public function installEntitiesDatabase()
{
$dbm = $this->getDatabaseStructureManager();
if ($dbm->hasEntities()) {
$dbm->generateProxyClasses();
$dbm->installDatabase();
}
}
/**
* Installs a package's database from an XML file.
*
* @param string $xmlFile Path to the database XML file
*
* @return bool|stdClass Returns false if the XML file could not be found
*
* @throws DoctrineDBALConnectionException
*/
public static function installDB($xmlFile)
{
if (!file_exists($xmlFile)) {
return false;
}
$db = Database::connection();
$db->beginTransaction();
$parser = Schema::getSchemaParser(simplexml_load_file($xmlFile));
$parser->setIgnoreExistingTables(false);
$toSchema = $parser->parse($db);
$fromSchema = $db->getSchemaManager()->createSchema();
$comparator = new DoctrineDBALSchemaComparator();
$schemaDiff = $comparator->compare($fromSchema, $toSchema);
$saveQueries = $schemaDiff->toSaveSql($db->getDatabasePlatform());
foreach ($saveQueries as $query) {
$db->query($query);
}
$db->commit();
$result = new stdClass();
$result->result = false;
return $result;
}
/**
* Updates the available package number in the database.
*
* @param string $vNum New version number
*/
public function updateAvailableVersionNumber($vNum)
{
$db = Database::connection();
$v = array($vNum, $this->getPackageID());
$db->query("update Packages set pkgAvailableVersion = ? where pkgID = ?", $v);
}
/**
* Returns the package ID.
*
* @return int|null
*/
public function getPackageID()
{
return $this->pkgID;
}
/**
* Sets the package ID.
*
* @param int|null $value
*/
public function setPackageID($value)
{
$this->pkgID = empty($value) ? null : (int) $value;
}
/**
* Updates a package's name, description, version and ID using the current class properties.
*/
public function upgradeCoreData()
{
$db = Database::connection();
$p1 = static::getClass($this->getPackageHandle());
if ($p1 instanceof self) {
$v = array(
$p1->getPackageName(),
$p1->getPackageDescription(),
$p1->getPackageVersion(),
$this->getPackageID(),
);
$db->query("update Packages set pkgName = ?, pkgDescription = ?, pkgVersion = ? where pkgID = ?", $v);
}
}
/**
* Upgrades a package's database and refreshes all blocks.
*/
public function upgrade()
{
$this->upgradeDatabase();
// now we refresh all blocks
$items = $this->getPackageItems();
if (is_array($items['block_types'])) {
foreach ($items['block_types'] as $item) {
$item->refresh();
}
}
Localization::clearCache();
}
/**
* Updates a package's database using entities and a db.xml.
*
* @throws DoctrineDBALConnectionException
* @throws Exception
*/
public function upgradeDatabase()
{
$this->destroyProxyClasses();
$this->installEntitiesDatabase();
static::installDB($this->getPackagePath() . '/' . FILENAME_PACKAGE_DB);
}
/**
* Moves the current package's directory to the trash directory renamed with the package handle and a date code.
*/
public function backup()
{
// you can only backup root level packages.
// Need to figure something else out for core level
if ($this->pkgHandle != '' && is_dir(DIR_PACKAGES . '/' . $this->pkgHandle)) {
$trash = Config::get('concrete.misc.package_backup_directory');
if (!is_dir($trash)) {
mkdir($trash, Config::get('concrete.filesystem.permissions.directory'));
}
$trashName = $trash . '/' . $this->pkgHandle . '_' . date('YmdHis');
$ret = rename(DIR_PACKAGES . '/' . $this->pkgHandle, $trashName);
if (!$ret) {
return array(self::E_PACKAGE_MIGRATE_BACKUP);
} else {
$this->backedUpFname = $trashName;
}
}
}
/**
* If a package was just backed up by this instance of the package object and the packages/package handle directory doesn't exist, this will restore the
* package from the trash.
*/
public function restore()
{
if (strlen($this->backedUpFname) && is_dir($this->backedUpFname) && !is_dir(DIR_PACKAGES . '/' . $this->pkgHandle)) {
return @rename($this->backedUpFname, DIR_PACKAGES . '/' . $this->pkgHandle);
}
return false;
}
}