Sitecore IsPageEditor and IsExperienceEditor - sitecore

We are currently writing a module for Sitecore and have ran into a problem.
We have a pipeline in which we do the following check:
if (Sitecore.Context.PageMode.IsExperienceEditor)
{
return;
}
The problem is that one of our clients are running and older version of Sitecore (8.0 update 5) where the property IsExperienceEditor does not exist yet. See Sitecore release notes for next update where it is introduced.
To quickly fix the error we used the older deprecated property which is this:
if (Sitecore.Context.PageMode.IsPageEditor)
{
return;
}
Now the question is, is there any way in which we can test for the Sitecore version so we can have backwards compatibility in the module?

You can use the code which is executed in Sitecore in background of both properties mentioned by you:
if (Sitecore.Context.Site.DisplayMode == Sitecore.Sites.DisplayMode.Edit)
{
return;
}
I know that using Sitecore.Context.PageMode.IsExperienceEditor (or Sitecore.Context.PageMode.IsPageEditor) is more elegant, but in a situation when you need to support both old and new Sitecore versions, that's sounds like a good option.

The deprecated property of IsPageEditor is still present specifically for the purpose of backward compatibility. IsExperienceEditor is just a renamed property that does the same thing that IsPageEditor does.
However you can check for the existence of a property like this:
public static bool HasProperty(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
Another option is to make two different versions of the module, if the implementation becomes significantly different for the different versions of Sitecore.

Related

Eclipse CDT: Problems with extension point CIndexer

Problem 1: I can't find org.eclipse.cdt.core.index.IIndexer
From the API:
API Information: Plug-ins that want to extend this extension point must implement org.eclipse.cdt.core.index.IIndexer interface.
Is the API Information incorrect/deprecated? Which interface should be implemented if not IIndexer?
Problem 2: I can install my own Indexer in CDT version 6.8 (eclipse 2019-06) but not in version 6.5 (eclipse 2018-09), though I don't see the difference in the plugin code.
More Details:
My Indexer class:
#SuppressWarnings("restriction")
public class MyIndexer extends PDOMFastIndexer {
public static final String ID = "de.blub.MyIndexer";
#Override
public String getID() {
return ID;
}
#Override
public IPDOMIndexerTask createTask(ITranslationUnit[] added, ITranslationUnit[] changed,
ITranslationUnit[] removed) {
if (...) {
return new MyIndexerTask(added, changed, removed, this, true);
} else {
return super.createTask(added, changed, removed);
}
}
The plugin.xml
<extension
id="org.eclipse.cdt.core.fastIndexer"
name="My Indexer"
point="org.eclipse.cdt.core.CIndexer">
<run
class="de.blub.MyIndexer">
</run>
The MANIFEST.MF File lists org.eclipse.cdt.core in the Require-Bundle section without bundle-version. Of course the cdt plugin has different versions:
In Eclipse 2019-06:
Eclipse CDT C/C++ Development Tools Core 6.8.1.201907021957 org.eclipse.cdt.core
In Eclipse 2018-09:
Eclipse CDT C/C++ Development Tools Core 6.5.0.201811180605 org.eclipse.cdt.core
This code is from org.eclipse.cdt.internal.core.pdom.PDOMManager:
private IPDOMIndexer newIndexer(String indexerId, Properties props) throws CoreException {
IPDOMIndexer indexer = null;
// Look up in extension point
IExtension indexerExt = Platform.getExtensionRegistry().getExtension(CCorePlugin.INDEXER_UNIQ_ID, indexerId);
if (indexerExt != null) {
IConfigurationElement[] elements = indexerExt.getConfigurationElements();
for (IConfigurationElement element : elements) {
if ("run".equals(element.getName())) { //$NON-NLS-1$
try {
indexer = (IPDOMIndexer) element.createExecutableExtension("class"); //$NON-NLS-1$
indexer.setProperties(props);
} catch (CoreException e) {
CCorePlugin.log(e);
}
break;
}
}
}
// Unknown index, default to the null one
if (indexer == null)
indexer = new PDOMNullIndexer();
return indexer;
}
The code is the same for both cdt versions. indexer becomes a PDOMFastIndexer in eclipse 2018-09, but a MyIndexer in 2019-06.
One difference I could see is that in RegistryObjectManager
private Object basicGetObject(int id, byte type) {
Object result = cache.get(id);
if (result != null)
return result;
...
}
An id is used to get the correct ConfigurationElement (result) from a cache object, which I don't really understand how it is built up. However, the returned ConfigurationElement contains a field propertiesAnsValues which is incorrect in the one case (org.eclipse.cdt.internal.core.pdom.indexer.PDOMFastIndexer instead of de.blub.MyIndexer).
How can I fix that to have my own indexer in eclipse 2018-09, too?
Please also note my Problem 1. Because if the API description is correct, it means I'm trying to install my indexer the wrong way and need to do something to 'see' the IIndexer interface.
According to the schema definition, the class you need to derive from is IPDOMIndexer (which you're already doing). You can also tell this from the PDOMManager code you quoted, which casts the result of createExecutableExtension() to IPDOMIndexer.
(The comment saying to use org.eclipse.cdt.core.index.IIndexer is indeed out of date. Based on a brief look, that interface hasn't existed since at least 2005. Patches to update the extension point documentation are welcome.)
As for your second problem, I believe it's because you're using id="org.eclipse.cdt.core.fastIndexer" for your extension, which is already in use by one of CDT's built-in indexers. The id needs to identify your extension uniquely (so you can make it something like myproject.MyIndexer.)

LLVM (3.5+) PassManager vs LegacyPassManager

I'm working on a new language using the LLVM C++ API and would like to take advantage of optimization passes. (Note: I'm currently using the latest from source LLVM which I believe equates to 3.8)
I have yet to find any examples that use the new PassManager and even Clang is still utilizing the LegacyPassManager.
I have come across posts such as this that are several years old now that mention the new PassManager, but they all still use the legacy system.
Is there any examples/tutorials on how to use this new(ish) PassManager? Should new LLVM projects prefer PassManager to LegacyPassManager? Does Clang plan on migrating or is this why the Legacy system has stuck around?
From what I've gathered with help from the #llvm IRC:
FunctionPassManager FPM;
//Use the PassInfoMixin types
FPM.addPass(InstCombinePass());
//Register any analysis passes that the transform passes might need
FunctionAnalysisManager FAM;
//Use the AnalysisInfoMixin types
FAM.registerPass([&] { return AssumptionAnalysis(); });
FAM.registerPass([&] { return DominatorTreeAnalysis(); });
FAM.registerPass([&] { return BasicAA(); });
FAM.registerPass([&] { return TargetLibraryAnalysis(); });
FPM.run(*myFunction, FAM);
But to avoid the hassle of manually registering each pass you can use PassBuilder to register the analysis passes
FunctionPassManager FPM;
FPM.addPass(InstCombinePass());
FunctionAnalysisManager FAM;
PassBuilder PB;
PB.registerFunctionAnalyses(FAM);
FPM.run(*myFunction, FAM);
Extending Lukes answer, with PassBuilder you can build predefined "out of box" simplification pipelines with different optimization levels:
llvm::FunctionAnalysisManager FAManager;
llvm::PassBuilder passBuilder;
passBuilder.registerFunctionAnalyses(FAManager);
passBuilder.buildFunctionSimplificationPipeline(
llvm::PassBuilder::OptimizationLevel::O2,
llvm::PassBuilder::ThinLTOPhase::None);
which will add a bunch of passes to FunctionAnalysisManager. This may simplify your life. The best place to see the full set of passes added for each OptimizationLevel is the original sources.

Enumerate Upgrade Codes of installed products?

I'd like to get list of all Upgrade codes of all installed products on Windows box. The question is: is there a dedicated MSI function to address this request?
There is MsiEnumProducts() that enumerates all installed products and MsiEnumRelatedProducts() to enumerate all products for the given Upgrade code. But I can't find a function to get all Upgrade codes in the system.
The workaround I can imagine is use MsiEnumProducts() to get list of all installed products, open each with MsiOpenProduct() function and read "UpgradeCode" property with MsiGetProductProperty(). But this should be very slow due to multiple MsiOpenProduct() calls.
I believe MsiEnumProducts loop with MsiOpenProduct and then MsiGetProductProperty is the correct official sequence. If you really need faster and are willing to bypass the API's you could read the registry directly at HKCR\Installer\UpgradeCodes. You'll have to reverse the Darwin Descriptors though. This isn't technically supported but the reality is these keys have been there for 16 years and MSFT has been doing ZERO development on The Windows Installer. Ok, maybe they updated the version number and removed ARM support in Windows 10 LOL.
FWIW, I like to use C# not C++ but the concept is the same. The following snippet ran on my developer machine in about 2 seconds.
using System;
using Microsoft.Deployment.WindowsInstaller;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
foreach (var productInstallation in ProductInstallation.AllProducts)
{
using(var database = new Database(productInstallation.LocalPackage, DatabaseOpenMode.ReadOnly))
{
Console.WriteLine(database.ExecutePropertyQuery("UpgradeCode"));
}
}
}
}
}
According to the DTF documentation, ProductInstallation.AllProducts uses MsiEnumProducts. The Database class constructor is using MsiOpenDatabase and ExecutePropertyQuery is a higher level call that basically abstracts doing a SELECT Value from Property WHERE Property = '%s'. So it'll be calling APIs to create, execute and fetch results from views. All these classes implement IDisposable to call the correct APIs to free resources also.
Ya... that's why I love managed code. :)

Sitecore Glass Mapper always null

I am using Sitecore Glass Mapper for a new project I'm setting up.
We are using Sitecore 7.2, latest version of Team Development for Sitecore (TDS) code generation and the latest version of glass.
The code I am trying to execute:
var b = new SitecoreContext();
var c = b.GetCurrentItem<T01_Homepage>();
b is not null. c is null.
var d = b.GetItem<T01_Homepage>("path")
d is null.
I added my assembly in GlassMapperScCustom:
public static IConfigurationLoader[] GlassLoaders(){
var attributes = new AttributeConfigurationLoader(new[] { "Company.Framework.Websites.Corporate", "Company.Framework.Core", "Company.Framework.Common" });
return new IConfigurationLoader[] { attributes };
}
When I look into b.GlassContext.TypeConfigurations all my models are there.
I figured it could be a language issue because the site is in dutch and maybe the wrong language would be resolved incorrectly. This was also not the case.
I disabled WebActivator and added the GlassMapperSc.Start() in my Global.asax Application_Start method.
We are also using Autofac as DI framework. But without it, it still isn't working as you can see above. Also when I create my own custom models without TDS code generation the result of GetCurrentItem<T> is null.
Does anyone have an idea how I can fix this?
Did you check your Sites.config and the default language for this website? There could be a difference between the language which is defined in your Sitecore languages folder and your configuration.
I had a similar problem with one of my projects where I changed the Sitecore.Context.Language to "nl" instead of "nl-NL". The glass mapper will return null, but Sitecore.Context.Database.GetItem will return an object in this case.
Most of the times it is a language issue. The mapper returns a null object when you do not have versions in the current or given language.
What can be confusing is that Sitecore.Context.Database.GetItem returns an object, even if it does not have a version in the current language. Be sure to check that item.Versions has any.
Some things you may try (this didn't fit in the comments field)
1) Confirm that the related fields in the Sitecore Item object contain values (so Sitecore.Context.Item for your "c" var and Sitecore.Context.Database.GetItem("path") for your "d" var)
2) Try to encapsulate the GetItem/GetCurrentItem call in a VersionCountDisabler, like this:
T01_Homepage model = null;
using (new VersionCountDisabler())
{
var context = new SitecoreContext();
model = context.GetItem<T01_Homepage>("path");
}
// Do you have data in model now?
3) Try to encapsulate the same call with a SecurityDisabler. Just to confirm it's not a security issue.
4) If you still don't know what it is, please update your question with some (simplified) code for your model.

CRecordset::snapshot doesn't work in VS2012 anymore - what's the alternative?

Apparently, in VS2012, SQL_CUR_USE_ODBC is deprecated. [Update: it appears that the cursors library has been removed from VS2012 entirely].
MFC's CDatabase doesn't use it anymore (whereas it was the default for VS2010 and earlier versions of MFC), but instead uses SQL_CUR_USE_DRIVER.
Unfortunately, SQL_CUR_USE_DRIVER doesn't work properly with the Jet ODBC driver (we're interacting with an Access database). The driver initially claims to support positional operations (but not positional updates), but when an attempt is made to actually query the database, all concurrency models fail until the MFC library drops down to read-only interaction with the database (which is not going to fly for us).
Questions
Is this MS's latest attempt to force devs to move away from Jet based datasources and migrate to SQL Express (or the like)?
Is there another modality that we should be using to interact with our Access databases through VS 2012 versions of MFC/ODBC?(1)
See also:
http://social.msdn.microsoft.com/Forums/kk/vcmfcatl/thread/acd84294-c2b5-4016-b4d9-8953f337f30c
Update: Looking at the various options, it seems that the cursor library has been removed from VS2012's ODBC library. Combined with the fact that Jet doesn't correctly support positional updates(2), it makes "snapshot" mode unusable. It does appear to support "dynaset" as long as the underlying tables have a primary key. Unkeyed tables are incompatible with "dynaset" mode(3). So - I can stick with VS 2010, or I can change my tables to include an autonumber or something similar in order to ensure a pkey is available so I can use dynaset mode for the recordsets.
(1) e.g. should we be using a different open type for CRecordset? We currently use CRecordset::snapshot. But I've never really understood the various modes snapshot, dynamic, dynaset. A quick set of "try each" has failed to get a working updatable interface to our access database...
(2) it claims to when queried initially, but then returns errors for all concurrency modes that it previously claimed to support
(3) "dynamic" is also out, since Jet doesn't support it at all (from what I can tell from my tests).
I found a solution that appears to work. I overrode OpenEx the exact same way VS 2012 has it because we need that to call the child version of AllocConnect since it is not virtual in the parent. I also overrode AllocConnect as mentioned. In the derived version of CDatabase, try the following code:
MyCDatabase.h
BOOL OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions = 0);
void AllocConnect(DWORD dwOptions);
MyCDatabase.cpp
BOOL MyCDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
{
ENSURE_VALID(this);
ENSURE_ARG(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
ENSURE_ARG(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));
// Exclusive access not supported.
ASSERT(!(dwOptions & openExclusive));
m_bUpdatable = !(dwOptions & openReadOnly);
TRY
{
m_strConnect = lpszConnectString;
DATA_BLOB connectBlob;
connectBlob.pbData = (BYTE *)(LPCTSTR)m_strConnect;
connectBlob.cbData = (DWORD)(AtlStrLen(m_strConnect) + 1) * sizeof(TCHAR);
if (CryptProtectData(&connectBlob, NULL, NULL, NULL, NULL, 0, &m_blobConnect))
{
SecureZeroMemory((BYTE *)(LPCTSTR)m_strConnect, m_strConnect.GetLength() * sizeof(TCHAR));
m_strConnect.Empty();
}
// Allocate the HDBC and make connection
AllocConnect(dwOptions);
if(!CDatabase::Connect(dwOptions))
return FALSE;
// Verify support for required functionality and cache info
VerifyConnect();
GetConnectInfo();
}
CATCH_ALL(e)
{
Free();
THROW_LAST();
}
END_CATCH_ALL
return TRUE;
}
void MyCDatabase::AllocConnect(DWORD dwOptions)
{
CDatabase::AllocConnect(dwOptions);
dwOptions = dwOptions | CDatabase::useCursorLib;
// Turn on cursor lib support
if (dwOptions & useCursorLib)
{
::SQLSetConnectAttr(m_hdbc, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_ODBC, 0);
// With cursor library added records immediately in result set
m_bIncRecordCountOnAdd = TRUE;
}
}
Please note that you do not want to pass in useCursorLab to OpenEx at first, you need to override it in the hacked version of AllocConnect.
Also note that this is just a hack but it appears to work. Please test all your code to be sure it works as expected but so far it works OK for me.
If anyone else runs into this issue, here's what seems to be the answer:
For ODBC to an Access database, connect using CDatabase mydb; mydb.OpenEx(.., 0), so that you ask the system not to load the cursor library.
Then for the recordsets, use dynaset CMyRecordset myrs; myrs.Open(CRecordset::dynaset, ...).
Finally, you must make sure that your tables have a primary key in order to use dynasets (keysets).
Derive CDatabase and override OpenEx. In your derived class CMyDatabase, replace the call to AllocConnect to MyAllocConnect. Obviously, your MyAllocConnect function should call SQLSetConnectOption with the desired parameter:
// Turn on cursor lib support
if (dwOptions & useCursorLib)
{
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC));
// With cursor library added records immediately in result set
m_bIncRecordCountOnAdd = TRUE;
}
Then use your CMyDatabase class instead of CDatabase for all your queries.