I am working on a content migration project , from Ektron 9 to EpiServer 8, the first task is to migrate the content of specific pages , to achieve that, I was following Ektron's API guidance Ektron Developer API
1- I am approaching this migration the right way? right now I just added Ektron Dll as a reference in my app. I tried to use their web services , but it doesn't have the data i need (content of specific pages).Ektron Web Services
here's a snippet of my code:
GetAllTemplatesRequest cc = new GetAllTemplatesRequest();
//var UserCRUD = new Ektron.Cms.Framework.User.UserManager();
var UserCRUD = new UserManager();
string Token = UserCRUD.Authenticate("admin", "password");
if (!string.IsNullOrEmpty(Token)) // Success
{
try
{
//Create the Content Object set to observe permissions
Ektron.Cms.Framework.Content.ContentManager ContentAPI
= new Ektron.Cms.Framework.Content.ContentManager(ApiAccessMode.Admin);
//Retrieve the content
Ektron.Cms.ContentData contentData;
contentData = ContentAPI.GetItem(30);
//Output the retrieved item's content
var cs = contentData.Html;
}
catch (Exception _e)
{
throw _e;
}
}
else // Fail
{
}
But I am getting this error:
This is what i ended up doing :
As there are many ways to perform the migration , I chose the approach of focusing mainly on EpiServer APIs to create new content , blocks, and assets ; and get all the content i need from Ektron using SQL statements.
Ektron Save all the content in a table named content .
The pages are organized in “Folder structure” fashion , so every page is in a “Folder”
to get the folder ID for a specific page, you can use this query :
select folder_id from content where content_id = 2147485807
with that folder id you can get all the pages listed under that specific folder; for instance you will need to get all the pages under “Articles”.
Then i used that folderID in this query:
SELECT [content_id]
,[content_title]
,[content_html]
,[date_created]
,folder_id
,[content_teaser]
,[content_text]
,[end_date]
,[content_type]
,[template_id]
, content_status
FROM content
where folder_id=(select folder_id from content where content_id = 2147485807)
order by content_title
FOR XML PATH(‘Article’), ROOT (‘Articles’)
which Creates an XML for me ready to consume in my EpiServer code.
The first thing in EPI server I did is to add a new property in the SitePageBase model to add a “LegacyContentID” that will serve as a mapping entry in case i need to access/modify the content of the new created pages. it serves as a link between the data imported from Ektron and the new data i am creating on EPI server.
[Display(
Name = “Legacy Content ID”,
Description = “Content ID from Ektron imported data , for migration purposes”,
GroupName = GroupNames.PageSettings,
Order = 37)]
[ScaffoldColumn(false)]
[Editable(false)]
public virtual string LegacyContentID { get; set; }
Then i created a method to create the article pages , the only parameter it needs is the parentID from IP server (you can create a new page in EpiServer and then under properties of that page you can the page ID).
public void CreateArticlesPages(int parentID)
{
IContentRepository contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
var parentlink = new ContentReference(parentID);
XmlDocument doc = new XmlDocument();
doc.LoadXml(File.ReadAllText(#”Articles.xml”));
string jsonText = JsonConvert.SerializeXmlNode(doc);
dynamic data = JsonConvert.DeserializeObject(jsonText);
for (int i = 0; i < data.Articles.Article.Count; i++)
{
var PageImportedObject = data.Articles.Article[i];
ArticlePage page = contentRepository.GetDefault<ArticlePage>(parentlink);
page = contentRepository.GetDefault<ArticlePage>(parentlink);
page.LegacyContentID = PageImportedObject.content_id;
page.Name = PageImportedObject.content_title;
page.PageTitle = PageImportedObject.content_title;
if (PageImportedObject.content_teaser == null)
page.Summary = “No Summary from the Ektron DB”;
else
page.Summary = PageImportedObject.content_teaser;
page.Description = PageImportedObject.content_html.root.Description;
contentRepository.Save(page, EPiServer.DataAccess.SaveAction.Save, EPiServer.Security.AccessLevel.NoAccess);
contentRepository.Save(page, EPiServer.DataAccess.SaveAction.Publish, EPiServer.Security.AccessLevel.NoAccess);
}
}
The code above creates a new page of type “ArticlePage” and add content from the XML generated earlier holding Ektron’s info.
Just coping one dll from an Ektron site into another site will not work.
The Web services idea was a better one. There are web service calls to get the content by id.
Alternatively you could write your own web service that runs inside the ektron site and uses the Ektron API to expose the data you want. Then call that service from the other site.
You will want to review the content migration starter kit. https://github.com/egandalf/ContentTransferStarterKit
Related
I have started using the Xamarin plugin for Visual Studio to create an Android app.
I have a local SQL database, and I want to call it to display data. I don't see how I can do this. Is it possible?
After thinking this was a trivial thing to do, I was proven wrong when I tried setup a quick test project. This post will contain a full tutorial on setting up a DB for an Android App in Xamarin that will come in handy as a reference for future Xamarin users.
At a glance:
Add Sqlite.cs to your project.
Add your database file as an Asset.
Set your database file to build as an AndroidAsset.
Manually copy the database file out of your apk to another directory.
Open a database connetion using Sqlite.SqliteConnection.
Operate on the database using Sqlite.
Setting up a local database for a Xamarin Android project
1. Add Sqlite.cs to your project.
Start by going to this repository and downloading Sqlite.cs; this provides the Sqlite API that you can use to run queries against your db. Add the file to your project as a source file.
2. Add DB as asset.
Next, get your DB and copy it into the Assets directory of your Android project and then import it into your project so that it appears beneath the Assets folder within your solution:
I'm using the Chinook_Sqlite.sqlite database sample renamed to db.sqlite from this site throughout this example.
3. Set DB to build as AndroidAsset.
Right click on the DB file and set it to build action AndroidAsset. This will ensure that it is included into the assets directory of the APK.
4. Manually copy DB out of your APK.
As the DB is included as an Asset (packaged within the APK) you will need to extract it out.
You can do this with the following code:
string dbName = "db.sqlite";
string dbPath = Path.Combine (Android.OS.Environment.ExternalStorageDirectory.ToString (), dbName);
// Check if your DB has already been extracted.
if (!File.Exists(dbPath))
{
using (BinaryReader br = new BinaryReader(Android.App.Application.Context.Assets.Open(dbName)))
{
using (BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int len = 0;
while ((len = br.Read(buffer, 0, buffer.Length)) > 0)
{
bw.Write (buffer, 0, len);
}
}
}
}
This extracts the DB as a binary file from the APK and places it into the system external storage path. Realistically the DB can go wherever you want, I've just chosen to stick it here.
I also read that Android has a databases folder that will store databases directly; I couldn't get it to work so I've just ran with this method of using an existing DB.
5. Open DB Connection.
Now open a connection to the DB through the Sqlite.SqliteConnection class:
using (var conn = new SQLite.SQLiteConnection(dbPath))
{
// Do stuff here...
}
6. Operate on DB.
Lastly, as Sqlite.net is an ORM, you can operate on the database using your own data types:
public class Album
{
[PrimaryKey, AutoIncrement]
public int AlbumId { get; set; }
public string Title { get; set; }
public int ArtistId { get; set; }
}
// Other code...
using (var conn = new SQLite.SQLiteConnection(dbPath))
{
var cmd = new SQLite.SQLiteCommand (conn);
cmd.CommandText = "select * from Album";
var r = cmd.ExecuteQuery<Album> ();
Console.Write (r);
}
Summary
And that's how to add an existing Sqlite database to your Xamarin solution for Android! For more information check out the examples included with the Sqlite.net library, its unit tests and the examples in the Xamarin documentation.
Here is the one that I'm using and it's working
install the Sqlite plugin
create interface to access different platforms services
create a model for the table
implement the interface that you created earlier on all of the
platform you want to use
use the plugin to create, get, insert, etc on your table
for more detailed information check this
I am in the process of building a website that in near future shall replace an existing web-site. The existing web-site contains aprox. 300 users which I need to "import" to the extranet-module (which I have bought) in composite.
Is there a way to batch create users to the extranet module?
Yes, you can import your existing user database. You can either do this by writing a script and have that execute on your web site or by directly manipulating the underlying SQL table / XML file (depending on what you use to store Composite C1 data). You can also build a provider that links your existing user database with Composite C1 Extranet.
Importing users programmatically: For a script approach please see methods like AddNewUser described on http://docs.composite.net/Packages/Community/Extranet/CompositeCommunityExtranetDeveloperGuide/Using-Extranet-Facade-Methods
You would write this script as web service, aspx page or similar which executes on the Composite C1 website.
If you are running the Extranet in a default setup expect the providerName to be "Default".
Manipulating the physical data store directly: This depends on what data store you are running on. I suggest you add the groups you want and a test user to help you recognize data when you look at the underlying XML files / SQL tables.
If you are running on XML (default) you should focus on the files named Composite.Community.Extranet.DefaultProvider.DataTypes.DefaultProvider*.xml located in the folder ~/App_Data/Composite/DataStores. There are 3 sush files, one for groups, one for users and one for the relation between users and groups.
If you are running on SQL Server you should focus on the 3 tables named Composite.Community.Extranet.DefaultProvider.DataTypes.DefaultProvider*
In both cases you would need to add new entries to the User table/xml file and matching group relations to the GroupUser table/xml file. When you add a user you provide a unique ID and this ID you reuse to register the user in GroupUser.
When you have made your changes you can force Composite C1 to reload by using the Tools | Restart Server command in the C1 Console. If you make a backup of files/tables before you make changes you can easily revert by restoring the backup (in case you need to start over).
Writing a user/group provider: If your user data is in an external store and you would like to keep it there you could also make a bridge between this existing user store and the Composite C1 Extranet by creating a custom provider. If this is relevant see http://docs.composite.net/Packages/Community/Extranet/CompositeCommunityExtranetDeveloperGuide/Writing-Custom-Extranet-Providers
Thank you. It now works. I imported the users programmatically. I opened the composite solution in visual studio and added a aspx page. Here is the code behind.
using System;
using System.Collections.Generic;
using System.IO;
using Composite.Community.Extranet;
using Composite.Community.Extranet.Data;
public partial class ImportUsers : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string path = Server.MapPath("UsersToImport.csv");
StreamReader _streamReader = new StreamReader(path);
IList<Guid> userIds = new List<Guid>();
string line;
while ((line = _streamReader.ReadLine()) != null)
{
string[] fields = line.Split(',');
IExtranetUser extranetUser = new ExtranetUser();
extranetUser.Name = fields[0];
extranetUser.UserName = fields[1];
extranetUser.Email = fields[2];
extranetUser.IsApproved = true;
IExtranetUser addedUser = ExtranetFacade.AddNewUser("Default", extranetUser);
userIds.Add(addedUser.Id);
ExtranetFacade.SetUsersForGroup("Default", new Guid("bc728100-e28e-4135-a14c-bead6e0b9b00"), userIds);
Response.Write(string.Format("User: {0} added at {1}", addedUser.UserName, addedUser.CreationDate));
}
}
}
I'm using a WCF data Service to access a MSSQL database. If the client requests data (e.g. from the table "Projects") i build my cache like this:
var collection = new ObservableCollection<Project>();
foreach (var project in this.Entities.Project)
{
collection.Add(project);
}
return collection;
If I want to refresh the list I just call
collection.Clear();
and call the above method again. If I edit a project and refresh the list as described above it works fine, but if i change the data on one client instance and refresh the list on another one the service doesn't load the changed project.
How can I force the DataService to re- load a whole entity (e.g. "Projects") even if from the service's point of view nothing has changed?
Possible solution:
public partial class Entities
{
public void RefreshProject(Project pr)
{
this.Detach(pr);
pr = this.Project.Where(p => p.Id == pr.Id).Single();
}
}
Usage:
entities.RefreshProject(project);
I have created a Setup Project for my Project. This project connects to a live DB server through asmx services. That URL will be determined after the client will deploy the web services on some server. So in setup project i have added a "TextBoxes" dialog in User Interface Editor section of the Setup Project where i have enabled only one TextBox to get the URL of the deployed Services. In my project i have added a file to be executed during Setup installation and i have defined it as follows:
[RunInstaller(true)]
public class InstallerHelper : System.Configuration.Install.Installer
{
// Other Code also exists that is not needed to be shown here<br/>
//.....
// The following method gets executed during setup installation
public override void Install(IDictionary stateSaver)
{
try
{
base.Install(stateSaver);
//Proceed only if the Context object has some parameters
if (Context.Parameters.Count != 0 && !string.IsNullOrEmpty(Context.Parameters["WEBSITEURL"]))
{
//Get the installation Folder's Path
string installationFolder = Context.Parameters["INSTALLFOLDER"];
// Get the Site's URL entered by Client
string websiteUrl = Context.Parameters["WEBSITEURL"];
//Create different Key Value pairs based on entered URL
string[][] keyValues = {
new string[] {"SiteUrl",websiteUrl},
new string[] {"WebServiceURL", websiteUrl + "Users.asmx" },
new string[] {"TicketsServiceURL", websiteUrl + "Tickets.asmx"},
new string[] {"CampaignsAndProjetcsServiceURL", websiteUrl + "CampaignsAndProjetcs.asmx"},
new string[] {"EntitiesURL", websiteUrl + "Entities.asmx"},
new string[] {"AccountsURL", websiteUrl + "Accounts.asmx"},
new string[] {"TransactionsURL", websiteUrl + "Transactions.asmx"},
new string[] {"RelatedReportsURL", websiteUrl + "RelatedReports.asmx"},
new string[] {"GiftAidsURL", websiteUrl + "GiftAids.asmx"}
};
// Load the app.Config file and store these values in it.
//********************************************
string configFilePath = installationFolder + #"\MyProject.exe.config";
XmlDocument configuration = new XmlDocument();
// Load App.Config File
configuration.Load(configFilePath);
//Add the values in it
Utility.UpdateValue(keyValues, configuration);
//Save configuration File
configuration.Save(configFilePath);
//********************************************<br/>
}
}
catch (Exception ex)
{
throw new InstallException("The following Error(s) occured during installation. \n " + ex.Message);
}
}
}
Here i Store the entered URL and some other generated URLs of different web services in App.Config of the Project to be used in Project for accessing data.
It works fine when i install a fresh copy of the Setup, but problem occurs when i try to Repair the installed project by again executing the Setup.exe file.
Repair process does not asks me to enter the URL again and also the Items stored in App.Config during first time installation are lost. So the whole application stops working.
Any help is greatly appreciated
A good approach is to save this custom information somewhere and retrieve it during maintenance using searches:
create some string registry entries which contain your custom properties; the registry entry value can be something like:
[WEBSITEURL]
create registry searches which read these entries and save them in your custom properties; for this use the property names for the actual searches
This way a repair will read the property values from registry and restore your original properties.
Both the Registry Editor and Launch Conditions Editor can be opened by selecting your setup project in Solution Explorer and clicking the appropriate button on its top pane.
I have a Visual Studio solution with a Silverlight project, and a web project which hosts the Silverlight app. The web project also contains an ASMX web service which is called by the Silverlight ap.
As described below, certain calls to the web service work fine, and yet others cause a CommunicationException to be thrown, wrapping a WebException - both with the message "The server returned the following error: 'not found'".
Firstly, here's my original method, which failed as described above (entity names changed for simplicity):
[WebMethod]
public Customer GetCustomer(int id)
{
CustomerDataContext dc = new CustomerDataContext();
return dc.Customers.SingleOrDefault(x => x.Id == id);
}
Secondly, to debug the problem I took Linq to SQL and the database out of the picture, and the below code worked fine:
[WebMethod]
public Customer GetCustomer(int id)
{
Customer c = new Customer() { ID=1, Name="Bob", History = new EntitySet<CustomerHistory>() };
return c;
}
Third, thinking about this, one difference between the two methods is that the first one would include values in the customer history. I extended the second method to include this, and it started failing again:
[WebMethod]
public Customer GetCustomer(int id)
{
Customer c = new Customer() { ID=1, Name="Bob", History = new EntitySet<CustomerHistory>() };
c.History.Add(new CustomerHistory() { Id=1, CustomerId=1, Text="bla" });
return c;
}
I'm stuck with regards to how to progress - my current thinking is that this could be a deserialization issue on the Silverlight side, when the object graph is deeper. This rationally doesn't make sense, but I can't think of anything else. I've confirmed that the transfer size and buffer size are big enough (2GB by default).
Any pointers would be appreciated.
Ahhhh the famous "Not Found" error, try to get details from that error using the tag in your web.config. That will create a log file providing details of the error.
The following link explains exaclty how to do it :
http://blogs.runatserver.com/lppinson/post/2010/04/15/Debugging-WCF-Web-Services.aspx