Showing posts with label AX - Useful Things. Show all posts
Showing posts with label AX - Useful Things. Show all posts
Thursday, 19 December 2013
Sunday, 15 December 2013
Add Image to Resource Node in AX and use it
adding image through resource
Upload image:
Use resource files in Axapta
In Application Object Tree, you can find resources node
.
Select resources node and right click; select Create from File, specify the file location for the new resource
Select resources node and right click; select Create from File, specify the file location for the new resource
file. After that you can use this resource file in Axapta without specifying an absolute file path in your
local/server system.
First, pick up the resource node from AOT;
SysResource::getResourceNode();
Then generate a temporary file for this resource file;
SysResource::saveToTempFile()
Finally specify the temporary file path for controls.
First, pick up the resource node from AOT;
SysResource::getResourceNode();
Then generate a temporary file for this resource file;
SysResource::saveToTempFile()
Finally specify the temporary file path for controls.
Here comes an example to show how to use a resource file as a background image of a given form.
{
ResourceNode resourceNode;
FilePath imagename;
;
resourceNode = SysResource::getResourceNode(resourcestr(ResourceName));
if (resourceNode)
{
resourceNode. AOTload();
imagename = SysResource::saveToTempFile(resourceNode);
}
else
{
throw Error(“No file exists.”)
}
element.design().imageName(imagename);
}
Example:
You can try "Resources" node to achieve your requirement.
Just Add a new resources under "Resources" node in AOT, than use following X++ code to access that image..
SysResource::getResourceNodeData(SysResource::getResourceNode('theaxapta_File'));
Tuesday, 29 October 2013
Create New user in Active directory
How to create Active Directory Users:
Administrator Tools >> Active Directory Users and Computers
Click on User >> To create new user in Active Directory ..
Once you create user then only you can add user to AX...
Click on the user you created for ex: sujju - RC on it >> properties..
Click on Member of - you must include administrator permission, to get administrator permissions to the user.
Else ax will not be open due to no granted permissions.
Administrator Tools >> Active Directory Users and Computers
Click on User >> To create new user in Active Directory ..
Once you create user then only you can add user to AX...
Click on the user you created for ex: sujju - RC on it >> properties..
Click on Member of - you must include administrator permission, to get administrator permissions to the user.
Else ax will not be open due to no granted permissions.
Tuesday, 15 October 2013
List Page Interaction Class
List page and form menuitem enable code
Listpage
interaction class:
Code for Action pane button enable and disabling:
1. selectionChanged()
if(NetAgreement.Status == NetAgreementStatus::Approved
&& NetAgreement.FinalizeAgree == NoYes::No)
this.listPage().actionPaneControlEnabled(formControlStr(NetAgreementList,FinalizeAgreement),true);
else
this.listPage().actionPaneControlEnabled(formControlStr(NetAgreementList,FinalizeAgreement),false);
2. selectionChanged()
public void
selectionChanged()
{
super();
this.setButtonEnabled();
if(netReservationTable.RentalApprovalState ==
ApprovalState::WorkflowCompleted)// &&
NetReservationListPage.& NetAgreement.FinalizeAgree == NoYes::No)
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),true);
else
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),false);
}
Action pane button enabling and disabling in form:
Form –datasource-(table of data source)-active method:
1.Active():
if(NetAgreement.Status ==
NetAgreementStatus::Approved && NetAgreement.FinalizeAgree ==
NoYes::No)
FinalizeAgreement.enabled(true);
else
FinalizeAgreement.enabled(false);
//ended by poorna...04/09/2013
return ret;
2.Active():
if(NetReservationTable.RentalApprovalState
== ApprovalState::WorkflowCompleted)//&&
NetAgreement.FinalizeAgree == NoYes::No)
btnReserve.enabled(true);
else
btnReserve.enabled(false);
=====================================================Listpage interaction class: example
Listpage
interaction class:( NetReservationListPageInteraction-interaction class,
NetReservationListPage-listpage
name , btnReserve-button name(autodeclaration yes),
NetReservation_NoFilter-listpage
query, NetReservationTable-listpage table)
Class declaration:
public class
NetReservationListPageInteraction extends
ListPageInteraction
{
smmLeadTable
smmLeadTable;
smmActivityParentLinkTable
smmActivityParentLinkTable;
QueryBuildRange
leadProcessRange;
HierarchyName selectedProcess;
NetReservationTable
netReservationTable;
}
selectionChanged:
public void
selectionChanged()
{
super();
this.setButtonEnabled();
if(netReservationTable.RentalApprovalState ==
ApprovalState::WorkflowCompleted)// &&
NetReservationListPage.& NetAgreement.FinalizeAgree == NoYes::No)
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),true);
else
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),false);
}
setButtonEnabled:
void setButtonEnabled ()
{
boolean isCustomer;
smmOpportunityTable
smmOpportunityTable;
boolean enableContacts = false;
DirOrgPersonRelations
dirOrgPersonRelations;
netReservationTable = this.listPage().activeRecord(queryDataSourceStr(NetReservation_NoFilter,
NetReservationTable));
/* isCustomer =
DirPartyTable::isCustomer(netReservationTable.Party);
if (!netReservationTable.RecId ||
!netReservationTable.Party || isCustomer)
{
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,createCustomer),false);
}
else
{
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,createCustomer),true);
}
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,customerButton),netReservationTable.RecId
!= 0 && isCustomer);
*/
}
setDBEnabled:
public void
setDBEnabled()
{
boolean isDraft;
;
/*
// DNS-Hussain on 12-09-2013
netReservationTable =
this.listPage().activeRecord(queryDataSourceStr(NetReservation_NoFilter, NetReservationTable));
isDraft =
netReservationTable.RentalApprovalState == ApprovalState::NotSubmitted;
if(NetReservationTable.RentalApprovalState
!= ApprovalState::NotSubmitted)
{
NetReservationTable_ds.allowEdit(isDraft);
NetReservationTable_ds.allowDelete(isDraft);
}
// end.
this.setButtonEnabled();
if(netReservationTable.RentalApprovalState
== ApprovalState::WorkflowCompleted)// && NetReservationListPage.&
NetAgreement.FinalizeAgree == NoYes::No)
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),true);
else
this.listPage().actionPaneControlEnabled(formControlStr(NetReservationListPage,btnReserve),false);
*/
==========================================================================================================
listpage enabling override methods(How to write methods on the list page Form in Ax 2012)
for enabling default override methods in list page we will set on property to group as
display target-- client(defautly it is auto we can change as client)
display target-- client(defautly it is auto we can change as client)
Scenario: I have a menu item that calls some class to do manipulation on the list page Form and I need to refresh the list page now. List pages do not allow writing of code on the Form, as they used interaction class.
Solution: You can write the code on list page buttons by setting DisplayTarget property to “Client” from auto. However after this change, you won’t be able to sue this button on the EP, so if you are thinking of using the same list page on the EP and planning to use the same button there, then do not do it. However f your domain is only the Form then you are good to do this.
Any ways override the clicked method and call research() of the data source to do this

How to delete a label file in Dynamics AX 2012
In this post we will look at how can we delete a label file in Dynamics AX 2012.Labels are no longer a file system like we had in 2009. We now have a new node in AOT - Label Files.
If you search in your application folder, you wont find the label file there.The application folder is located here."C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\ Application\ Appl\Standard".
In Dynamics AX 2012, the label file is imported in your current model.
Now let us restart the AOS and see what happens. After restarting the AOS, go back to your application folder and try to find the label file again. This time the label file will be found.On every AOS restart, all label files will be copied to the application folder from the model store. So your label files are contained in your models but they are stored in your application folder as well.
Now let us try to delete the label file. Right click on your label file. Do you see a delete/remove option?
The answer is "No". so how do we delete the label file then? Below is the method by which a label file can be deleted easily.
- To create a temporary model
- Move our label file to the temporary model
- Stop the AOS
- Delete the temporary model
- Start the AOS
Let 's do step by step.
Go to Tools -> Model management -> Create model.
Enter name as TmpModel. Press OK. You will recieve an info message
saying “The model TmpModel was created successfully in layer usr.”
Right click on your label file and select “Move to model”.
Select the TmpModel checkbox and press OK.
Stop the AOS.
Open command prompt (Microsoft Dynamics AX 2012 Management Shell). We
will be using the command line utility AXUTIL to delete the model.
Issue the following command in the command prompt.
AXUTIL delete /model:TmpModel
AXUTIL delete /model:TmpModel
You will be prompted if you want to delete the model or not. Press Y
Start the AOS.
Delete the label file from your application folder as well.
When you open AX now, you will get a dialog saying that “Your model store has been modified.”
This is normal because each time you perform an operation on a model,
you will get this dialog. Depending on your operation, you should select
one of the option. Since we just deleted a model which just had a label
file, select Skip.Open AOT and notice that the label file is deleted now.
Sending Mail from Outlook
Sending
email in AX is now as easy as 123.There are various ways by which we
can send email in Dynamics AX. The code snippet shared here allows the
user to send email through Microsoft Outlook using X++ code.The code is simple and easy to understand.
Description255 recipientEmail;
Notes emailBody;
Description255 subjectText;
Filename fileName;
SmmOutlookEmail smmOutlookEmail = new SmmOutlookEmail();
recipientEmail = "axuser@hotmail.com";
subjectText = "Test Email";
fileName = @"C:\Users\admin\Desktop\mypic.jpg";
emailBody = "Hi,\nThis is a test email for Dyanmics AX.\nThanks.";
if (smmOutlookEmail.createMailItem())
{
smmOutlookEmail.addEMailRecipient(recipientEmail);
smmOutlookEmail.addSubject(subjectText);
smmOutlookEmail.addFileAsAttachment(fileName);
smmOutlookEmail.addBodyText(emailBody);
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,true);
}
else
{
error("Could not communicate with Microsoft Outlook Client.");
}
So, if you want to send email directly without opening in Outlook, replace
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,true);
with
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,false);
===============================================================================

Hi,
static void AppointmentFrom
Outlook(Args _args)
{
COM sysOutlookCollection;
COM receipiants;
COM collection;
COMVariant comStartDate = new COMVariant();
COMVariant comEndDate = new
COMVariant();
COM c;
#SysOutLookCOMDEF
#define.mapi("MAPI")
#define.outlook("Outlook.Application")
COM sysOutlook;
COM sysOutlookNameSpace;
COM sysOutlookMAPIFolder;
sysOutlook = new COM(#outlook);
sysOutlookNameSpace = sysOutlook.getNamespace(#mapi);
sysOutlookNameSpace.logon();
sysOutlookMAPIFolder = sysOutlookNameSpace.getDefaultFolder(#OlDefaultFolders_olFolderTasks);
collection = sysOutlookMAPIFolder.items();
c = collection.add();
comStartDate.date(today());
comStartDate.time(str2Time( "12:00:00"));
comEndDate.date(today());
comEndDate.time(str2Time( "12:15:00"));
c.subject("This is the subject");
c.body("Body of that msg");
c.save();
if (c)
{
receipiants = c.Recipients();
receipiants.add("mdalfasith@gmail.com");
receipiants.ResolveAll();
c.assign();
//c.display();
c.send();
info("Success msg in AX");
}
else
throw error("@SYS31969");
sysOutlookNameSpace.logoff();
}
Description255 recipientEmail;
Notes emailBody;
Description255 subjectText;
Filename fileName;
SmmOutlookEmail smmOutlookEmail = new SmmOutlookEmail();
recipientEmail = "axuser@hotmail.com";
subjectText = "Test Email";
fileName = @"C:\Users\admin\Desktop\mypic.jpg";
emailBody = "Hi,\nThis is a test email for Dyanmics AX.\nThanks.";
if (smmOutlookEmail.createMailItem())
{
smmOutlookEmail.addEMailRecipient(recipientEmail);
smmOutlookEmail.addSubject(subjectText);
smmOutlookEmail.addFileAsAttachment(fileName);
smmOutlookEmail.addBodyText(emailBody);
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,true);
}
else
{
error("Could not communicate with Microsoft Outlook Client.");
}
So, if you want to send email directly without opening in Outlook, replace
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,true);
with
smmOutlookEmail.sendEMail(smmSaveCopyOfEMail::No,false);
===============================================================================
Send task msg from current outlook in Dynamic AX

Hi,
static void AppointmentFrom
Outlook(Args _args)
{
COM sysOutlookCollection;
COM receipiants;
COM collection;
COMVariant comStartDate = new COMVariant();
COMVariant comEndDate = new
COMVariant();
COM c;
#SysOutLookCOMDEF
#define.mapi("MAPI")
#define.outlook("Outlook.Application")
COM sysOutlook;
COM sysOutlookNameSpace;
COM sysOutlookMAPIFolder;
sysOutlook = new COM(#outlook);
sysOutlookNameSpace = sysOutlook.getNamespace(#mapi);
sysOutlookNameSpace.logon();
sysOutlookMAPIFolder = sysOutlookNameSpace.getDefaultFolder(#OlDefaultFolders_olFolderTasks);
collection = sysOutlookMAPIFolder.items();
c = collection.add();
comStartDate.date(today());
comStartDate.time(str2Time( "12:00:00"));
comEndDate.date(today());
comEndDate.time(str2Time( "12:15:00"));
c.subject("This is the subject");
c.body("Body of that msg");
c.save();
if (c)
{
receipiants = c.Recipients();
receipiants.add("mdalfasith@gmail.com");
receipiants.ResolveAll();
c.assign();
//c.display();
c.send();
info("Success msg in AX");
}
else
throw error("@SYS31969");
sysOutlookNameSpace.logoff();
}
Select Statement Queries
Select Statement Syntax for AX
SelectStatement
|
=
|
select Parameters
|
Parameters
|
[ [ FindOptions ] [ FieldList from ] ]
TableBufferVariable [ IndexClause ] [ Options ] [ WhereClause ] [ JoinClause ]
|
|
FindOptions
|
=
|
crossCompany | reverse | firstFast | [ firstOnly | firstOnly10 | firstOnly100 | firstOnly1000 ] | forUpdate | noFetch | [forcePlaceholders | forceLiterals] |forceselectorder | forceNestedLoop | repeatableRead
|
FieldList
|
=
|
Field { , Field } | *
|
Field
|
=
|
Aggregate ( FieldIdentifier ) | FieldIdentifier
|
Aggregate
|
=
|
sum | avg | minof | maxof | count
|
Options
|
=
|
[ order by , group by , FieldIdentifier [ asc | desc ] { , FieldIdentifier [ asc | desc ] }] | [ IndexClause ]
|
IndexClause
|
=
|
index IndexName | index hint IndexName
|
WhereClause
|
=
|
where Expression
|
JoinClause
|
=
|
[exists | notexists | outer ] join Parameters
|
Keyword
|
Description
|
Example
|
asc
|
An option on the order by or group by clause. The sorting is ascending. (Sort is
ascending by default.)
|
select * from custTable
order by
Name asc;
|
avg
|
Returns the average of the fields.
|
CustTable custTable;
;
select avg(value) from custTable;
print custTable.value;
|
count
|
Returns the number of records.
|
CustTable xCT;
int64 iCountRows; ;
Select COUNT(RecID) from xCT;
iCountRows = xCT.RecID;
|
crossCompany
|
Returns data for all companies that
the user is authorized to read from. (A container can be added to reduce the
number of companies involved.)
|
CustTable custTable;
container conCompanies =
['dat','dmo'];
;
select crossCompany :conCompanies
* from
custTable;
|
desc
|
An option on the order by or group by clause. The sorting is descending.
|
select * from custTable
order by
Name desc;
|
exists
|
A method that returns a Boolean
value and a join clause.
|
while select AccountNum, Name
from custTable
order by
AccountNum
exists
join * from ctr
where
(ctr.AccountNum ==
custTable.AccountNum)
|
firstFast
|
A priority hint. The first row
appears more quickly but the total return time for this option might be
slower. The firstFast hint is automatically issued from all
forms, but is rarely used directly from X++.
|
select firstFast custTable
order by
AccountNum;
|
firstOnly
|
Speeds up the fetch. Instructs
MorphX to fetch only the first record.
|
static InventTable find(
ItemId itemId,
boolean update
= false)
{
InventTable
inventTable;
;
inventTable.selectForUpdate
(update);
if
(itemId)
{
select
firstonly inventTable
index
hint ItemIdx
where
inventTable.itemId
==
itemId;
}
return
inventTable;
}
|
firstOnly10
|
Same as firstOnly, except
returns 10 rows instead of one.
|
|
firstOnly100
|
Same as firstOnly, except
returns 100 rows instead of one.
|
|
firstOnly1000
|
Same as firstOnly, except
returns 1000 rows instead of one.
|
|
forceLiterals
|
Note
You are advised not to use
the forceLiterals keyword in X++ select statements, because it could expose code to an SQL injection
security threat.
forceLiterals instructs the
kernel to reveal the actual values that are used in where clauses to the Microsoft SQL Server database at the time of
optimization.
forceLiterals and forcePlaceholders are
mutually exclusive.
|
|
forceNestedLoop
|
Forces the Microsoft SQL Server
database to use a nested-loop algorithm to process a particular SQL statement
containing a join algorithm. This means that a record from the first table is
fetched before any records from the second table are fetched. Typically,
other join algorithms, such as hash-joins and merge-joins, would be
considered. This keyword is often combined with the forceSelectOrder keyword.
|
while select forceSelectOrder
forceNestedLoop
inventTransThis
index
hint TransIdIdx
where
inventTransThis.InventTransId
==
inventTrans.InventTransId
&&
inventTransThis.StatusIssue
<=
StatusIssue::ReservOrdered
|
forcePlaceholders
|
Instructs the kernel not to reveal
the actual values used in where clauses to the SQL Server
database at the time of optimization. This is the default in all statements
that are not join statements.
The advantage of using this keyword
is that the kernel can reuse the access plan for other similar statements
with other search values. The disadvantage is that the access plan is
computed without taking into consideration that data distribution might not
be even. The access plan is an on-average access plan.
forcePlaceholders and forceLiterals are
mutually exclusive.
|
static void
forcePlaceHoldersExample(Args _args)
{
SalesTable
salesTable;
SalesLine
salesLine;
;
while select
forcePlaceholders salesLine
join
salesTable
where
salesTable.SalesId ==
salesLine.SalesId
&&
salesTable.SalesId == '10'
{
//more
code
}
}
|
forceSelectOrder
|
Forces the SQL Server database to
access the tables in a join in the specified order. If two tables are joined,
the first table in the statement is always accessed first. This keyword is
often combined with the forceNestedLoop keyword.
|
display ForecastHasPurch
hasForecastPurch()
{
ForecastPurch forecastPurch;
InventDim inventDim;
;
select
firstOnly forcePlaceholders
forceSelectOrder
recId
from
forecastPurch
index
hint ItemIdx
where
forecastPurch.itemId == this.itemId
exists
join inventDim
index
hint DimIdIdx
where
inventDim.inventDimId ==
forecastPurch.inventDimId
&&
inventDim.configId == this.configId;
return
forecastPurch.recId;
}
|
forUpdate
|
Selects records exclusively for update.
Depending on the underlying database, the records may be locked for other
users.
|
ttsBegin;
while select forUpdate
ledgerJournalTrans
index
hint NumVoucherIdx
where
ledgerJournalTrans.journalNum ==
_journalNum
&&
ledgerJournalTrans.voucher
== _voucher
{
ledgerJournalTrans.doDelete();
counter++;
}
if (counter
&&
ledgerJournalTable.journalType
!=
LedgerJournalType::Periodic)
{
NumberSeq::release(
ledgerJournalTable.voucherSeries,
_voucher);
}
ttsCommit;
|
group by
|
Instructs the database to group
selected records by fields.
|
CustTable custTable;
;
while select sum(CreditMax) from
custTable
group by
CustGroup
{
print
custTable.CustGroup, " ",custTable.CreditMax;
}
|
index
|
Instructs the database to sort the
selected records as defined by the index.
|
CustTable custTable;
;
while select AccountNum, Name
from custTable
index
AccountIdx
{
print
custTable.AccountNum, " ", custTable.Name;
}
|
index hint
|
Gives the database a hint to use
this index to sort the selected records as defined by the index. The database
can ignore the hint.
Note
A wrong index hint can have a big
performance impact. Index hints should only be applied to SQL statements that
do not have dynamic where clauses or order by clauses, and where the effect of the hint can be verified.
|
while select forUpdate
ledgerJournalTrans
index
hint NumVoucherIdx
where
ledgerJournalTrans.journalNum
==
_journalNum
|
join
|
Used to join tables on a column
that is common to both tables. The join criteria are specified in the where clause
because there is no on clause in X++ SQL.
Reduces the number of SQL statements
that are needed if you want to loop through a table and update transactions
in a related table.
For example, if you process 500
records in a table, and want to update related records in another table, and
use a nested while select to do this, there will be 501 trips to the
database. If you use a join, there will be a single trip to the
database.
|
while select ledgerTable
join
ledgerTrans
where
ledgerTrans.accountNum ==
ledgerTable.accountNum
{
amountMST
+= ledgerTrans.amountMST;
}
|
maxof
|
Returns the maximum of the fields.
|
CustTable custTable;
;
select maxof(CreditMax) from
custTable;
|
minof
|
Returns the minimum of the fields.
|
CustTable custTable;
;
select minof(CreditMax) from
custTable;
|
noFetch
|
Indicates that no records are to be
fetched at present. This is typically used when the result of the select is
passed on to another application object, for example, a query that performs
the actual fetch.
|
select noFetch custTable
order by AccountNum
|
notExists
|
Chosen only if there are no posts.
|
while select AccountNum, Name
from custTable
order by
AccountNum
notExists
join * from ctr
where
(ctr.AccountNum ==
custTable.AccountNum)
|
optimisticLock
|
Forces a statement to run with
Optimistic Concurrency Control even if a different value is set on the table.
For more information, see Optimistic Concurrency Control.
|
select optimisticLock custTable
where
custTable.AccountNum > '1000'
|
order by
|
Instructs the database to sort the
selected records by fields in the order by list.
|
select * from custTable
order by
accountNum desc
where
custTable.AccountNum > "100";
|
outer
|
Returns all rows from the
first-named table, including rows that have no match in the second-named
table. This is a left outer join, although there is no left keyword.
There is no right outer join in X++ SQL.
|
while select AccountNum, Name
from
custTable
order by
AccountNum
outer
join * from ctr
where
ctr.AccountNum == custTable.AccountNum
|
pessimisticLock
|
Forces a statement to run with
Pessimistic Concurrency Control even if a different value is set on the
table.
|
select pessimisticLock custTable
where
custTable.AccountNum > '1000'
|
repeatableRead
|
Specifies that no other
transactions can modify data that has been read by logic inside the current transaction,
until after the current transaction completes.
An explicit transaction completes
at either ttsAbort or at the outermost ttsCommit.
For a stand-alone select statement,
the transaction duration is the duration of the select command. However,
the database sometimes enforces the equivalent of repeatableRead in
individual select statements even without this keyword appearing in
your X++ code (depending on how the database decides to scan the tables).
|
For more information, see the
documentation for the underlying relational database product.
|
reverse
|
Records are returned in reverse
order.
|
select reverse custTable
order by
AccountNum;
|
sum
|
Returns the sum of the fields. Can
be used to sum all accounts, order lines, and so on.
|
CustTable custTable;
;
select sum(CreditMax) from
custTable;
|
select containerLine.*
from WHSCONTAINERLINE containerLine
inner join WHSWORKTABLE workTable on workTable.CONTAINERID = containerLine.CONTAINERID
inner join WHSLOADLINE whsloadline on whsloadline.RECID = containerLine.LOADLINE
inner join WHSWORKINVENTTRANS trans on trans.INVENTTRANSIDPARENT = whsloadline.INVENTTRANSID and trans.WORKID = workTable.WORKID
where containerLine.CONTAINERID = '0000156938'
Subscribe to:
Posts (Atom)