After a Tridion CMS Database restore from another environment we cannot unpublish Components from the Broker. If we publish to the Broker then we can unpublish. We want to set the IsPublishedTo status to the publish targets available in the new envioronment.
The TOM API has a SetPublishedTo method available for Pages and Component Templates but not Components.
How can I set the PublishedStatus for the Components? Is it possible using UpdateXML or do we need to perform database black magic?
I use the following C# based code in a command line tool for switching the PublishStates of all my items after a SDL Tridion 2009 environment switch (What version are you using?):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tridion.ContentManager.Interop.TDS;
using Tridion.ContentManager.Interop.TDSDefines;
using System.Xml;
namespace SetAllItemsAsUnpublished
{
/// <summary>
/// A command line script that can enable/disable users
/// </summary>
class Program
{
static void Main(string[] args)
{
TDSE tdse = new TDSE();
User currentUser = tdse.User;
ListRowFilter listRowFilter = tdse.CreateListRowFilter();
String xpath = "/tcm:ListPublishItems/*/*[local-name()='Page' or local-name()='Component']";
listRowFilter.SetCondition("Recursive", true);
listRowFilter.SetCondition("OnlyPublishedPages", true);
listRowFilter.SetCondition("OnlyPublishedCPs", true);
//listRowFilter.SetCondition("ItemType", ItemType.ItemTypePage);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr.AddNamespace("tcm", "http://www.tridion.com/ContentManager/5.0");
//Check that the user running the script is an Administrator
if (currentUser.privileges == TDSPrivileges.TdsPrivilegeSystemAdministrator)
{
Publications publications = tdse.GetPublications();
Console.WriteLine("There are " + publications.Count + " to be processed");
int i = 0;
foreach (Publication publication in tdse.GetPublications())
{
++i;
Console.WriteLine(" - Processing " + publication.Title + "(" + i + " of " + publications.Count + ")");
foreach( PublicationTarget target in tdse.GetPublicationTargets()){
Console.Write(" checking target: " + target.Title);
XmlDocument publishedItemsXml = new XmlDocument();
try
{
publishedItemsXml.LoadXml(publication.GetListPublishItems(target.ID, false, false, ListColumnFilter.XMLListID, listRowFilter));
foreach (XmlElement publishedItemNode in publishedItemsXml.SelectNodes(xpath, nsmgr))
{
String uri = publishedItemNode.Attributes["ID"].Value;
Console.Write(".");
if (publishedItemNode.LocalName == "Page")
{
Page page = (Page)tdse.GetObject(uri, EnumOpenMode.OpenModeView, publication, XMLReadFilter.XMLReadAll);
page.SetPublishedTo(target, false, currentUser);
if (page.Info.IsCheckedOut)
{
page.CheckIn(true);
}
}
else
{
foreach (XmlElement ctRenderNode in publishedItemNode.SelectNodes("tcm:RenderWith", nsmgr))
{
String uriCT = ctRenderNode.Attributes["ID"].Value;
ComponentTemplate ct = (ComponentTemplate)tdse.GetObject(uriCT, EnumOpenMode.OpenModeView, publication, XMLReadFilter.XMLReadAll);
ct.SetPublishedTo(uri, target, false, currentUser);
if (ct.Info.IsCheckedOut)
{
ct.CheckIn(true);
}
}
}
}
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
else
{
//Warn when there is a non-admin user running the script
Console.WriteLine("You must be an SDL Tridion CMS Administrator to run this application");
}
Console.WriteLine();
Console.WriteLine("Done! Hit ENTER key to close");
Console.ReadLine();
}
}
}
So basically setting the CT to UnPublished should do what you need, as the Component is not technically published, it is a Component Presentation based on that CT.
Components themselves are never published from Tridion, they are only published as part of a Component Presentation (Component + Component Template).
The SetPublishedTo method on a Component Template takes a Component as a parameter. So by calling it you can set one Component Presentation as Published or Unpublished.
Once you've unpublished all Component Presentations of a Component, that Component implicitly becomes Unpublished.
Related
I am running a small app in SharePoint that produces the data needed for someone in our company. In short it pulls data from an Azure database into a data-table, and then converts that data-table to excel. This can take about a minute or two and sometimes, because we are in New Zealand and are using a remote server in the USA, they get timeouts and one of the worksheets won't load.
So what it does when it builds the excel is it iterates through a list of suppliers, and a list of financial weeks getting the data for each and creating a separate worksheet in excel per data-table. Ideally, what I would like to add a new row to a grid view that the user sees, as the report is being built, stating whether that financial week and supplier was successfully added, or not, as the excel report is creating in the back-end. This would allow the user to be more aware of the progress, and allow them to know if there has been a problem rather than guessing.
It's a lot of code so I will try to show you the relevant parts.
Method that pulls and creates excel
public void excelThreadCall()
{
DataTable updateDataTable = new DataTable();
gridView.DataSource = updateDataTable;
//Payments only download chosen Financial Week
using (XLWorkbook workbook = new XLWorkbook())
{
//gradeWeek = selectedGradeWeek.SelectedValue;
foreach (ListItem supplier in selectedSuppliers.Items)
{
if (supplier.Selected)
{
foreach (ListItem fWeek in selectedfWeeks.Items)
{
if (fWeek.Selected)
{
string checkEmptyTableSQL = #"SELECT COUNT(*) FROM FleshvGraded WHERE Supplier_Code = '" + supplier.Value + "' AND PO_Revision = " + fWeek.Value;
int rowCount = Convert.ToInt32(getVariable(checkEmptyTableSQL));
if (rowCount > 0)
{
foreach (ListItem report in selectedReports.Items)
{
//SQL Strings
string sqlIntakeDate = #"SELECT Week_Ending_Date FROM Fiscal_Calendar WHERE Fiscal_Week = LEFT(" + fWeek + ", 2) AND Fiscal_Year = CONCAT(20, RIGHT(" + fWeek + ", 2))";
string sqlPO = #"SELECT DISTINCT PO_No FROM FvGSummaryAll WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value;
string sqlAllSerials = "SELECT * FROM FvGData WHERE Supplier_Code = '" + supplier.Value + "' AND f_Week = " + fWeek.Value
//variables
DateTime weekEnding = Convert.ToDateTime(getVariable(sqlIntakeDate));
DateTime weekStarting = weekEnding.AddDays(-5);
string fWeekString = fWeek.ToString();
string poNoString = getVariable(sqlPO).ToString();
string intakeDateString = weekStarting.Day + "/" + weekStarting.Month + "/" + weekStarting.Year + " to " + weekEnding.Day + "/" + weekEnding.Month + "/" + weekEnding.Year;
//adds summary variables to dictionary
Dictionary<string, string> summaryVariablesDict = new Dictionary<string, string>();
summaryVariablesDict.Add("f Week", fWeekString);
//other values added to Dict
//Adds WorkSheets based on above data
if (report.Selected && report.Value.Equals("allserials"))
{
string worksheetName = supplier.Value + " Data " + fWeek.Value;
DataTable dataTable = getDataTable(sqlAllSerials);
createWorkSheet(workbook, worksheetName, dataTable);
}
//Other Reports follow
**//what I hope to do - need this to show in the grid view immediatley not end of method
updateDataTable.Rows.Add(suppler, fweek, "successful");
gridView.DataBind();**
}
}
}
}
}
}
workbook.SaveAs(filePath);
}
}
So currently this exists in another class but it's no problem for me to move it to the aspx page, and so I have taken liberties to just show you what I need to do in this method. So if it doesn't make complete sense in that respect (i.e. I wouldn't declare the datasource for the grid in the method normally).
The problem I have is that it will wait until the end of the method before updating the grid view via the postback and then the user gets it all at once. I was hoping there is a way to update the gridview at each iteration or even every few seconds if we use a timer, but can't find a way to implement this.
So long story short, how can I update the gridview from this method where the results appear immediately on the users UI, and not wait until the end of the method.
I would do something along these lines:
When the page first loads, start a background thread to start building the spreadsheet.
When the page loads, call some JavaScript that kicks off a callback.
In the method in your code-behind that's called by the callback, check the status of the building process. Have that process maintain a list of strings, each representing the HTML for a row in a table.
Have the page (via JavaScript) perform a callback every few seconds. That callback will get the current list of rows. JavaScript on the page will receive the response and update the rendered table to include the new rows.
When the spreadsheet is done (or when a error occurs that aborts the creation process), show a success or failure message to the user.
If it would be helpful, I can provide some simple callback samples to get you going.
Edit: Added code sample:
Markup:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CallBackWebForm.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script type="text/javascript">
var callbackFrequency = 2000;
// Callback javascript
// To make callback to server, call CallServer();
// Receive response from server after callback
function ReceiveServerData(arg, context) {
// Parse the JSON that we got from the server
args = JSON.parse(arg);
// Add rows to table
$.each(args.TableRows, function (index, value) {
$('#table1').append(value);
});
// If we're done, show a message
if (args.DoneLoadingSpreadsheet)
$('#doneDiv').show();
// Otherwise, start a timer to call back again
else
window.setTimeout(function () { CallServer(); }, callbackFrequency);
}
$(document).ready(function() {
// Start the callback loop
window.setTimeout(function () { CallServer(); }, callbackFrequency);
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
Sample page with some progress-y stuff
</div>
<table id="table1">
<tr>
<th>Col 1</th>
<th>Col 2</th>
<th>Col 3</th>
</tr>
<!-- Rows inserted by Javascript will go here -->
</table>
<div id="doneDiv" style="display: none;">
All done!
</div>
</form>
</body>
</html>
Code-behind:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI;
using Newtonsoft.Json;
namespace CallBackWebForm
{
public partial class Default : System.Web.UI.Page, ICallbackEventHandler
{
protected void Page_Load(object sender, EventArgs e)
{
// Setup Callback javascript so that page can initiate callbacks and receive callback responses
CreateClientSideCallbackFunction();
if (!Page.IsPostBack)
StartBuildingSpreadsheetTask();
}
#region Callback
private void CreateClientSideCallbackFunction()
{
var cm = Page.ClientScript;
// The Javascript function in the markup must exactly match the function name as entered below (ReceiveServerData)
var cbReference = cm.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
// The Javascript function to be placed in the markup which will be used to initiate the callback
var callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);
}
/// <summary>
/// Called when CallServer(arg, context) is called in javascript on the page
/// </summary>
/// <param name="eventArgument">Not used, but must be passed</param>
public void RaiseCallbackEvent(string eventArgument)
{
}
/// <summary>
/// Called at the end of a callback; provides the response/result to the client
/// </summary>
/// <returns>JSON string representing an instance of the DataTransferClass</returns>
public string GetCallbackResult()
{
// Serialize the DataTransferObject, then delete all TableRows so we don't send them to the browser again
// Note: this is not currently thread-safe. You should add some sort of locking mechanism so the background thread
// doesn't modify the TableRows list while we're serializing it and deleting from it.
var dtoJson = JsonConvert.SerializeObject(DataTransferObject);
DataTransferObject.TableRows.Clear();
return dtoJson;
}
public class DataTransferClass
{
public bool DoneLoadingSpreadsheet { get; set; }
public List<string> TableRows { get; set; }
}
#endregion Callback
#region Background Task
// Sessions have unique IDs, but individual page views don't. So, create one for this page view.
private string ViewID
{
get
{
if (string.IsNullOrEmpty(ViewState["_viewID"] as string))
ViewState["_viewID"] = Guid.NewGuid().ToString();
return ViewState["_viewID"] as string;
}
}
// Store all DataTransfer data and token sources in static dictionaries so the background task can get to them
private static Dictionary<string, DataTransferClass> DataTransferDictionary = new Dictionary<string, DataTransferClass>();
private static Dictionary<string, CancellationTokenSource> TokenSourcesDictionary = new Dictionary<string, CancellationTokenSource>();
// Make the values in the dictionaries for this View easily accessible via Properties
private DataTransferClass DataTransferObject
{
get
{
if (DataTransferDictionary.ContainsKey(ViewID))
return DataTransferDictionary[ViewID];
else
return null;
}
set
{
if (DataTransferDictionary.ContainsKey(ViewID))
DataTransferDictionary[ViewID] = value;
else
DataTransferDictionary.Add(ViewID, value);
}
}
private CancellationTokenSource TokenSource
{
get
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
return TokenSourcesDictionary[ViewID];
else
return null;
}
set
{
if (TokenSourcesDictionary.ContainsKey(ViewID))
TokenSourcesDictionary[ViewID] = value;
else
TokenSourcesDictionary.Add(ViewID, value);
}
}
private void StartBuildingSpreadsheetTask()
{
DataTransferObject = new DataTransferClass() { DoneLoadingSpreadsheet = false, TableRows = new List<string>() };
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
(new TaskFactory()).StartNew(() => BuildSpreadsheet(ViewID, token), token);
}
private void BuildSpreadsheet(string viewID, CancellationToken token)
{
// Simulate work. Update DataTransferObject every 5 seconds, finish after 30 seconds (6 iterations with 5 second wait);
for (int i = 0; i < 6; i++)
{
// Work for 5 seconds
System.Threading.Thread.Sleep(5000);
// Update DataTransferObject with new row (don't use the 'DataTransferObject' property; it relies up the 'ViewID' property, which in
// turn relies upon ViewState, which isn't available from a background thread).
DataTransferDictionary[viewID].TableRows.Add("<tr><td>Val " + i + "</td><td>Val " + (i * 10) + "</td><td>Val " + (i * 100) + "</td></tr>");
}
// All done; update DataTransferObject
DataTransferDictionary[viewID].DoneLoadingSpreadsheet = true;
}
#endregion Background Task
}
}
A couple notes:
Add Json.Net NuGet package (Newtonsoft)
Note that the page class implements the ICallbackEventHandler interface
Edit 2: Updated suggested process at the top to match what I actually did in the code sample.
I am developing an application, which requires chrome browser history. I have written a C# code for fetching the history. However there are two issues in my code which I am unable to figure out.
There is this warning.
Warning 1 There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "System.Data.SQLite", "AMD64". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project. ChromeData
There is this error
SQLite error (5): database is locked
I tried closing the browser, but still there is this error. However, when I created a copy of History file and renamed it, gave its path instead of History, the program was working and it could read the file and fetch the data.
I am unable to figure it out where the error is. So, please help. I am posting my 3 class files.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data.SQLite;
using System.Data;
namespace ChromeData
{
class GoogleChrome
{
public List<URL> Urls = new List<URL>();
public IEnumerable<URL> GetHistory()
{
string DocumentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
//Console.WriteLine(DocumentsFolder);
string[] tempstr = DocumentsFolder.Split('\\');
foreach(string s in tempstr)
{
Console.WriteLine(s);
}
string tempstr1 = "";
DocumentsFolder += "\\Google\\Chrome\\User Data\\Default";
if(tempstr[tempstr.Length-1] != "Local")
{
for(int i =0; i<tempstr.Length-1;i++)
{
tempstr1 += tempstr[i] + "\\";
}
DocumentsFolder = tempstr1 + "Local\\Google\\Chrome\\User Data\\Default";
}
Console.WriteLine(DocumentsFolder);
if(Directory.Exists(DocumentsFolder))
{
return ExtractUserHistory(DocumentsFolder);
}
return null;
}
public IEnumerable<URL> ExtractUserHistory(string folder)
{
DataTable HistoryData = ExtractFromTable("urls", folder);
foreach(DataRow row in HistoryData.Rows)
{
string url = row["url"].ToString();
string title = row["title"].ToString();
URL u = new URL(url.Replace('\'',' '), title.Replace('\'',' '), "Google Chrome");
Urls.Add(u);
}
return Urls;
}
DataTable ExtractFromTable(string table, string folder)
{
SQLiteConnection sql_con;
SQLiteDataAdapter DB;
SQLiteCommand sql_cmd;
string dbpath = folder + "\\History";
DataTable DT = new DataTable();
if(File.Exists(dbpath))
{
try
{
sql_con = new SQLiteConnection("Data Source=" + dbpath + ";Version=3;New=False;Compress=True;");
sql_con.Open();
sql_cmd = sql_con.CreateCommand();
string CommandText = "select * from " + table;
DB = new SQLiteDataAdapter(CommandText, sql_con);
DB.Fill(DT);
sql_con.Close();
}
catch(Exception e)
{
TextWriter errorWriter = Console.Error;
errorWriter.WriteLine(e.Message);
}
}
return DT;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ChromeData
{
class TestClass
{
public static List<URL> Urls = new List<URL>();
public static void Main()
{
string path = #"C:\Users\Public\Desktop\history.txt";
GoogleChrome g = new GoogleChrome();
Urls = (List<URL>)g.GetHistory();
using(StreamWriter sw = File.CreateText(path))
{
foreach(URL u in Urls)
{
sw.WriteLine(u.url);
}
}
Console.ReadLine();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ChromeData
{
class URL
{
public string url;
public string title;
public string browser;
public URL(string url,string title,string browser)
{
this.browser = browser;
this.title = title;
this.url = url;
}
}
One solution is to copy the file to a temporary location and read it from there.
string source = #"C:\Users\{USERNAME}\AppData\Local\Google\Chrome\User Data\Default\History";
string target = #"C:\Temp\History";
if (File.Exists(target))
{
File.Delete(target);
}
File.Copy(source, target);
string cs = #"Data Source=" + target;
string sql = "Select * From urls";
using (SQLiteConnection c = new SQLiteConnection(cs))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine(rdr[1].ToString());
}
}
}
}
I've found chrome.exe will continue running, and holding the lock, despite exiting the browser as normal.
taskkill.exe /IM chrome.exe /F
This will shut down Chrome, with an added bonus of having a 'restore tabs' button upon restart by the user. Restore tabs is available because you killed forcefully.
Hi want to receive push notifications on background task for that i have created Portable library here is my Background task class
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Windows.ApplicationModel.Background;
using Windows.Data.Xml.Dom;
using Windows.Networking.PushNotifications;
using Windows.Storage;
using Windows.UI.Notifications;
namespace BackgroundTask
{
public sealed class NotificationTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Get the background task details
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
string taskName = taskInstance.Task.Name;
Debug.WriteLine("Background " + taskName + " starting...");
// Store the content received from the notification so it can be retrieved from the UI.
ToastNotification notification = (ToastNotification )taskInstance.TriggerDetails;
settings.Values[taskName] = notification.Content;
NotificationTask.AddTostNotification(notification.Content);
Debug.WriteLine("Background " + taskName + " completed!");
}
private static void AddTostNotification(String xmlDocument)
{
List<string> messageSection = NotificationTask.GetMessageAndLandingPage(xmlDocument, "toast");
if (messageSection == null) { return; }
ToastTemplateType toastTemplate = ToastTemplateType.ToastText01;
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
toastTextElements[0].AppendChild(toastXml.CreateTextNode(messageSection[0]));
// toastTextElements[1].AppendChild(toastXml.CreateTextNode(message));
IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
((XmlElement)toastNode).SetAttribute("launch", messageSection[1]);
XmlElement audio = toastXml.CreateElement("audio");
audio.SetAttribute("src", "ms-appx:///Assets/Play-Guitar.wav");
//audio.SetAttribute("loop", "true");
toastNode.AppendChild(audio);
//launch tost immediatly
ToastNotification toast = new ToastNotification(toastXml);
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
Here i am registering Task
internal async void InitChannel()
{
// Applications must have lock screen privileges in order to receive raw notifications
BackgroundAccessStatus backgroundStatus = await BackgroundExecutionManager.RequestAccessAsync();
// Make sure the user allowed privileges
if (backgroundStatus != BackgroundAccessStatus.Denied && backgroundStatus != BackgroundAccessStatus.Unspecified)
{
Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
try
{
var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
if (channel != null)
{
roamingSettings.Values["ExistingPushChannel"] = channel.Uri;
dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
channel.PushNotificationReceived += OnPushNotificationReceived;
UnregisterBackgroundTask();
RegisterBackgroundTask();
}
else
{
roamingSettings.Values["ExistingPushChannel"] = "Failed to create channel";
}
}
catch
{
roamingSettings.Values["ExistingPushChannel"] = "Failed to create channel";
}
}
}
private void RegisterBackgroundTask()
{
BackgroundTaskBuilder taskBuilder = new BackgroundTaskBuilder();
PushNotificationTrigger trigger = new PushNotificationTrigger();
taskBuilder.SetTrigger(trigger);
// Background tasks must live in separate DLL, and be included in the package manifest
// Also, make sure that your main application project includes a reference to this DLL
taskBuilder.TaskEntryPoint = "BackgroundTask.NotificationTask";
taskBuilder.Name = "PlaypushNotification";
try
{
BackgroundTaskRegistration task = taskBuilder.Register();
task.Completed += BackgroundTaskCompleted;
}
catch
{
UnregisterBackgroundTask();
}
}
private bool UnregisterBackgroundTask()
{
foreach (var iter in BackgroundTaskRegistration.AllTasks)
{
IBackgroundTaskRegistration task = iter.Value;
if (task.Name == "PlaypushNotification")
{
task.Unregister(true);
return true;
}
}
return false;
}
In my Manifest file
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTask.NotificationTask">
<BackgroundTasks>
<Task Type="pushNotification" />
</BackgroundTasks>
</Extension>
</Extensions>
PushNotification Trigger is not firing, when i debug i found that trigger property of BackgroundTaskRegistration is null. what is the issue? What wrong is going here?
I'm looking for a way to print ASP.NET/ Mono MVC2 view from ASP.NET application running in Windows 2003 server.
I tried code below based on Programmatically "hello world" default SERVER-side printer in ASP.NET MVC
but this outputs raw html string. How to print view as formatted text using free software?
Order layout is created as html partial view. If there is other free way to print out formatted order, I can create layout in other form instead of html.
Only free solution which I have found requires to use Windows Forms WebBrowser control but this looks not reasonable in MVC2 application which is running under Mono also.
I looked into Rotativa ( http://nuget.org/packages/Rotativa/ ) but it looks like it doesnt allow to print html.
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Web.Mvc;
public class PrintController : Controller
{
string body;
public ActionResult Complete()
{
body = RenderViewToString<TestOrder>("~/Views/Checkout/Order.ascx", new TestOrder() { Number = "1" });
PrintOrder();
return View("PaymentComplete");
}
void PrintOrder()
{
// https://stackoverflow.com/questions/12229823/programmatically-hello-world-default-server-side-printer-in-asp-net-mvc
var doc = new PrintDocument();
doc.PrinterSettings.PrinterName = "HP Laserjet 1200";
doc.PrintPage += new PrintPageEventHandler(ProvideContent);
doc.Print();
}
void ProvideContent(object sender, PrintPageEventArgs e)
{
e.Graphics.DrawString(body,
new Font("Arial", 12),
Brushes.Black,
e.MarginBounds.Left,
e.MarginBounds.Top);
}
string RenderViewToString<T>(string viewPath, T model)
{ // https://stackoverflow.com/questions/483091/render-a-view-as-a-string
ViewData.Model = model;
using (var writer = new StringWriter())
{
var view = new WebFormView(viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
}
public class TestOrder
{
public string Number;
}
There is an article about convert HTML to PDF using iTextSharp: http://www.dotnetspider.com/resources/43589-How-convert-HTML-PDF-ASP-NET.aspx
I have a schema in Tridion which have embedded schema fields which may further have embedded fields in there.
I want to reach final leaf field so that I can assign some value to it. For that I want to write recursive function which loop through each and every field until it reaches a final field.
I am implementing using the Core Service in SDL Tridion 2011
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ServiceModel;
using System.Net;
using System.Xml;
using Tridion.ContentManager.CoreService.Client;
using System.Text;
using Tridion.ContentManager.CoreService;
using System.ServiceModel.Channels;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Data.OleDb;
using System.Data;
using System.Configuration;
namespace Loading_Utility
{
public partial class TST : System.Web.UI.Page
{
Fields obj = new Fields();
protected void Page_Load(object sender, EventArgs e)
{
using (ChannelFactory<ISessionAwareCoreService> factory =
new ChannelFactory<ISessionAwareCoreService>("wsHttp_2011"))
{
ISessionAwareCoreService client = factory.CreateChannel();
var schemaFields = client.ReadSchemaFields("tcm:202-2242-8", true, new ReadOptions());
ComponentData component = (ComponentData)client.GetDefaultData(ItemType.Component, "tcm:202-638-2");
var fields = Fields.ForContentOf(schemaFields);
component.Schema.IdRef="tcm:202-2242-8";
}
}
public void fieldRecursion(Field field)
{
//var getFields = fields;
if (field.GetType() == typeof(EmbeddedSchemaFieldDefinitionData))
{
// code for checking further if field is embedded or not
//Field newField = field.GetSubFields().GetFieldElements( new ItemFieldDefinitionData() as Field)
//fieldRecursion(recursiveField);
}
//string fieldName = recursiveField.Name;
//fields[fieldName] = "HI";
}
}
}
Whilst I don't have the solution you are looking for, I see you're using the core service, personally I prefer to get hold of the Component XML (Component.Content) and parse/manipulate it as I need. Perhaps if you can paste the XML here I can drop it into one of my sample core service projects and send you a solution back?
In the event that doesn't help you, i've had a look at the api, and this should help you get going in the right path. Perhaps once you have a solution you could paste it here?
public void RecurseEmbeddedFields(SchemaFieldsData schemaFields)
{
foreach (ItemFieldDefinitionData field in schemaFields.Fields)
{
if (field.GetType() == typeof(EmbeddedSchemaFieldDefinitionData))
{
// check if this field contains more embedded fields
// if it does recurse
}
}
}
OK, I felt a bit guilty about not helping, but I still stand by my view that this is not a Tridion-related question and that you should try getting some more experience with general development practices.
Here's an example of how to load the Component's content, then read it recursively using Xml:
Xml of the component:
<Content xmlns="uuid:02395f72-acef-44e8-9c35-ff8c9f380251">
<EmbeddedSchema1>
<SomeField>Hello</SomeField>
<EmbeddedSchema2>
<ATextField>There</ATextField>
</EmbeddedSchema2>
</EmbeddedSchema1>
</Content>
Core Service code:
static void Main(string[] args)
{
SessionAwareCoreServiceClient client = new SessionAwareCoreServiceClient("wsHttp_2011");
ReadOptions readOptions = new ReadOptions();
ComponentData component = (ComponentData)client.Read("tcm:5-3234", readOptions);
Console.WriteLine("Find fields recursively");
XmlDocument content = new XmlDocument();
content.LoadXml(component.Content);
SchemaData schema = (SchemaData)client.Read(component.Schema.IdRef, readOptions);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("content", schema.NamespaceUri);
foreach (XmlElement node in content.SelectNodes("content:*", ns))
{
ReadContentRecursively(node, ns);
}
client.Close();
}
private static void ReadContentRecursively(XmlElement node, XmlNamespaceManager ns)
{
if(!string.IsNullOrEmpty(node.InnerText))
{
foreach (XmlNode innerNode in node)
{
if(innerNode is XmlText)
{
Console.WriteLine("Node " + node.Name + " with value \"" + innerNode.Value + "\"");
}
}
}
if(node.SelectNodes("content:*", ns).Count > 0)
{
foreach (XmlElement childNode in node.SelectNodes("content:*", ns))
{
Console.WriteLine("Found Field: " + childNode.Name);
ReadContentRecursively(childNode, ns);
}
}
}
Notice how ReadContentRecursively calls itself?
Hope this helps.