Property sales fall as house price growth slows
Property118

Property sales fall as house price growth slows
Higher mortgage rates have helped drive a 9% annual fall in sales agreed across the UK, according to Zoopla’s latest House Price Index.
Also, a quarter of homes listed for sale in September have already been on the market within the past year.
The index reveals that annual growth in property prices has slowed to 0.8%, the lowest since July 2024.
Average mortgage rates have reached 5.2%, their highest in three years, adding £150 a month to the average homebuyer’s repayments compared with the start of the year.
Pricing homes for sale
Zoopla’s executive director, Richard Donnell, said: “While key measures of housing market activity are lower than last year, there is still plenty of demand for homes.
“Buyers are simply more cautious and selective about what they view and offer.”
He added: “Sellers who factor in local market conditions and seek detailed advice from their local estate agents on how to set the asking price, can still find a buyer relatively quickly.
“Getting the right price from the outset is essential.”
More homes for sale
The number of homes for sale is 5% higher than a year ago, and six in 10 homes returning to the market have a lower asking price, with relistings most common among flats and larger houses.
In London, a third of flats coming onto the market have been advertised before, compared with fewer than one in 10 in Scotland.
Around three-quarters of Scottish homes find a buyer within three months; in London, just three in 10 do.
Flat prices keep falling
Flat prices have fallen for 15 consecutive months, while the average UK home now costs £273,000.
Scotland and the North East are among the exceptions, recording annual increases in flat values while prices for this property type fall across most regions.
More affordable two- and three-bedroom houses generally attract stronger demand, according to Zoopla, while flats and larger family homes face tougher conditions, particularly in southern England.
The property website expects annual house price growth to slow to around 0.5% by December and forecasts 1.1 million sales during 2026, compared with 1.2 million last year.
The post Property sales fall as house price growth slows appeared first on Property118.
View Full Article: Property sales fall as house price growth slows
Andy Burnham pledges crackdown on poor-quality PRS homes
Property118

Andy Burnham pledges crackdown on poor-quality PRS homes
The Prime Minister has warned landlords who rent out the “worst-quality homes” will be given warnings and could ultimately face having their properties taken over by councils.
Speaking at the Labour Party conference, Andy Burnham also said he would reform Right to Buy and tackle the empty homes crisis.
The comments come as the government announces plans to strengthen leaseholders’ rights, including tighter regulation of property agents and a cap on fees.
Rented homes in a poor state of repair
Mr Burnham claimed that too many homes in the private rented sector are in a poor state of repair.
Addressing the Labour Party conference, Mr Burnham said: “You will find rented homes in a poor state of repair on so many British streets. Homes which harm the health of the people living inside them and drag down the streets around them.
“The worst of it is often owned by absent landlords, who are paid through the benefits system but refuse to reinvest in the upkeep of their properties.
“It is time to call an end to this. Landlords who rent out the worst-quality homes will be given warnings to improve them. If they refuse, we will make it much easier for councils to acquire those homes, and we will legislate if necessary.”
Mr Burnham added: “We don’t let people sell food that damages people’s health, so why should we allow it with homes? From now on, we won’t. This is what I mean by taking back control with our communities.”
Council housebuilding revolution
Mr Burnham added that the government would reform Right to Buy and launch a council housebuilding revolution.
He said: “I am specific about council homes because we need to restore stronger democratic control over our housing stock.
“We will help councils build again and make it affordable by building on unused public land. We will reform Right to Buy, not taking the right away from anyone in an existing home, but removing it from the new homes we build, so they are there for generations to come.”
The news comes as the government have announced councils in England will be able to take control of homes left empty for six months under government plans to cut the current two-year threshold.
Councils will also no longer need to provide evidence relating to anti-social behaviour or community support when seeking an order through the Residential Property Tribunal.
Industry reaction
Timothy Douglas, head of policy and campaigns at Propertymark, said further clarity is needed on how the proposals would work in practice.
He said: “Addressing poorly maintained properties should remain a key concern, with local authorities across England working closely with residents, landlords and property agents to understand why properties may fall below the required standard.
“Every case will have different circumstances, so any proposals must be proportionate and recognise the individual circumstances of the property and those living in it.
“Further clarity is needed on how the proposals would work in practice, including carrying out remedial work with a tenant in situ, the responsibilities of different parties, and how properties would be managed if they passed into local authority control or ownership.
“With the new ‘Register Your Rental Property Service’ being implemented for landlords from December, councils will have greater access to information to support targeted, intelligence-led enforcement. The focus should be on making better use of existing tools rather than simply adding another layer of legislation and potentially damaging relationships between good landlords and government.
“Any new proposals should deliver a fair, proportionate and tailored approach that achieves sustainable, long-term outcomes.”
The post Andy Burnham pledges crackdown on poor-quality PRS homes appeared first on Property118.
View Full Article: Andy Burnham pledges crackdown on poor-quality PRS homes
Right to Rent changes for England’s landlords from October
Property118

Right to Rent changes for England’s landlords from October
Landlords in England who use digital verification services for Right to Rent checks must choose registered providers under a revised code taking effect on 1 October.
However, tenants cannot be disadvantaged for choosing acceptable paper documents instead.
The revised Home Office codes cover checking procedures and avoiding unlawful discrimination.
They update guidance on digital evidence, facial recognition and the treatment of applicants whose immigration status requires further checks.
Fair treatment rules
The Home Office’s discrimination code states: “Individuals must not be treated less favourably because a different right to rent checking route is required.”
It also warns landlords against favouring applicants simply because their status is easier to verify.
Those with a time-limited right to rent must receive equal treatment once they have demonstrated their entitlement, even where follow-up checks are necessary.
Nor should landlords insist on digital evidence from people who cannot use the Home Office online service.
Digital checking requirements
A digital verification provider must appear on the government register with confirmation that it can conduct right to rent checks.
Using such a provider remains optional, with other permitted checking methods still available.
Where facial recognition fails, landlords must give the applicant a reasonable opportunity to verify their identity.
For tenants with eVisas, landlords must use the Home Office online checking service, although the Landlord Checking Service can help where technical problems prevent verification.
Avoiding discrimination claims
Checks should be applied consistently, including to applicants whom landlords believe are British.
The code warns against assumptions based on someone’s accent, nationality, colour or ethnic origins.
Subject to business requirements, landlords should, where possible, keep an accommodation offer open for a reasonable period while an applicant obtains evidence of their right to rent.
Anyone alleging unlawful discrimination by a landlord or prospective landlord can bring a complaint before a county court.
The post Right to Rent changes for England’s landlords from October appeared first on Property118.
View Full Article: Right to Rent changes for England’s landlords from October
Should we understand why a property is empty before introducing more damaging legislation?
Property118

Should we understand why a property is empty before introducing more damaging legislation?
The Renters’ Rights Act has introduced new legislation to ensure some property is kept empty for a year. If a tenant is issued a section 8 for property sale purposes and the landlord subsequently changes their mind about the sale, the property cannot be re-let for a year.
Meanwhile, the government is contemplating legislation to take control of houses and re-let them, should they have been empty for six months.
Maybe it is too much to hope for, but could someone undertake a sensible project and first understand why property is empty before introducing more damaging statute?
Best wishes,
Paul
The post Should we understand why a property is empty before introducing more damaging legislation? appeared first on Property118.
View Full Article: Should we understand why a property is empty before introducing more damaging legislation?
Should we understand why a property is empty before introducing more damaging legislation?
Property118

Should we understand why a property is empty before introducing more damaging legislation?
The Renters’ Rights Act has introduced new legislation to ensure some property is kept empty for a year. If a tenant is issued a section 8 for property sale purposes and the landlord subsequently changes their mind about the sale, the property cannot be re-let for a year.
Meanwhile, the government is contemplating legislation to take control of houses and re-let them, should they have been empty for six months.
Maybe it is too much to hope for, but could someone undertake a sensible project and first understand why property is empty before introducing more damaging statute?
Best wishes,
Paul
The post Should we understand why a property is empty before introducing more damaging legislation? appeared first on Property118.
View Full Article: Should we understand why a property is empty before introducing more damaging legislation?
PRS Database tipping point – Turning landlord problems into cash in the bank
Property118

PRS Database tipping point – Turning landlord problems into cash in the bank
News of the PRS Database has been the last straw for some landlords.
Every landlord in England will need to sign up and register each property for £65 per property every year.
For landlords already dealing with the Renters’ Rights Act, licensing, tax changes and increasing compliance, it is another requirement to add to the list.
And for some, it may be the point where they start thinking seriously about selling up.
If that is you, this is where Landlord Sales Agency comes in.
You might want to sell but have a compliance problem to sort out first. You might have tenants in place and not know what to do with them. You might have an HMO licensing issue, a property that needs work or simply a portfolio that you want to sell without spending months dealing with estate agents, viewings and negotiations.
Whatever is holding up your decision to sell, speak to us before assuming you need to solve it yourself.
A recent Lincoln HMO is a good example of what can be achieved when a sale hits an unexpected problem.
The seller had planned a nine-bed HMO, but due to a misunderstanding regarding the layout, the council only issued a six-bed licence.
Two lenders then declined the deal.
For many sellers, that could have meant the sale falling apart and going back to square one.
We took a different approach.
Working with our specialist HMO broker, surveyor, lender contacts and the council, we secured an upgraded HMO licence to secure finance based on the future nine-bed configuration and the accepted offer price, enabling the sale to proceed once the required works were completed and the licensing position resolved.
The problem did not disappear. We simply found a practical way through it.
That is important because landlords do not always come to us with a perfect, ready-to-sell property.
Equally, you do not need to have a problem property to use us.
Whether you have one straightforward rental property, a group of well-performing tenanted houses, or a large portfolio with a mixture of property types, we can help.
We know landlords want cash in the bank fast, and selling can become a long-drawn-out process, particularly when going down the traditional estate-agent route.
That is why we don’t limit ourselves to one type of buyer or one way of selling.
Depending on the property, we can look at investors, other landlords, owner-occupiers and, where appropriate, even help an existing tenant buy the home themselves.
If your tenants are the reason you think you cannot sell yet, speak to us before starting an eviction.
Where a property requires vacant possession, we can work directly with existing tenants to try to find a solution. That can include negotiating rents where appropriate to avoid the need for eviction, helping tenants relocate and, in some circumstances, providing financial help towards securing somewhere else to live.
Alternatively, there may be no need for the tenant to leave at all. We can sell with tenants remaining in place and rent continuing to be collected until completion.
For suitable tenants who would like to buy their home, we can also explore whether we can help them become the buyer.
The point is that selling a rental property does not always have to start with an eviction, months of waiting or solving every compliance issue yourself.
No matter whether you have one property or an entire portfolio, the first step is finding out what your options actually are.
We can sell individual properties, entire portfolios or partial portfolios in bulk, allowing landlords to keep the properties they still want while releasing cash from those they no longer do.
If the PRS Database is the latest requirement that has made you question whether now is the time to sell, don’t wait until every problem has been solved before speaking to us.
You may have more options than you think.
For a free, no-obligation conversation about your property or portfolio and what we could do to get cash in the bank, contact Landlord Sales Agency today.
(function(){var el=document.getElementById(“ts-607776b5-3f7b-431c-b5cc-fc304603fee3″);if(!el)return;var b=document.body,h=document.documentElement;var dark=b.classList.contains(“dark-mode”)||b.classList.contains(“dark”)||b.classList.contains(“night-mode”)||h.classList.contains(“dark”);if(!dark){var bg=window.getComputedStyle(b).backgroundColor,m=bg.match(/d+/g);if(m)dark=(m[0]*0.299+m[1]*0.587+m[2]*0.114)<128;}el.setAttribute("data-theme",dark?"dark":"light");if(window.turnstile&&el.childElementCount===0){try{window.turnstile.render(el,{sitekey:el.getAttribute("data-sitekey"),theme:el.getAttribute("data-theme")});}catch(e){}}})();
(function(){
var uid = “crm-form-ba09a29b”;
var form = document.getElementById(uid + ‘-form’);
var wrap = document.getElementById(uid);
var msg = wrap.querySelector(‘.crm-message’);
var totalPages = 1;
var curPage = 0;
// ── Name casing ──────────────────────────────────────────────────────
// Tidy a name box as the visitor leaves it, so they are not staring at
// “JOHN SMITH” in their own form. Deliberately cosmetic: the API runs
// the same normalisation on submit and is the authority on what gets
// stored, so a disagreement here is harmless. Kept in step with
// packages/shared/src/names.ts.
var NAME_PARTICLES = ‘ van von de del della der den di da dal du la le el bin ibn al dos das do ter ten ‘;
var NAME_MACS = ‘ macdonald macintyre macleod macarthur macgregor macneil macpherson macrae mackinnon macqueen ‘;
function nameUp(s){ return s.replace(/[a-z]/i, function(c){ return c.toUpperCase(); }); }
function isParticle(w){ return NAME_PARTICLES.indexOf(‘ ‘ + w.toLowerCase() + ‘ ‘) >= 0; }
function nameRecase(atom){
if (!/[a-z]/i.test(atom)) return atom;
var dot = atom.charAt(atom.length – 1) === ‘.’ ? ‘.’ : ”;
var core = dot ? atom.slice(0, -1) : atom;
var lower = core.toLowerCase();
if (/^(?:II|III|IV|VI|VII|VIII|IX|XI|XII)$/.test(core.toUpperCase())) return core.toUpperCase() + dot;
if (lower === ‘jr’ || lower === ‘sr’) return nameUp(lower) + dot;
if (core.length === 1) return core.toUpperCase() + dot;
var mc = /^mc([a-z]{2,})$/.exec(lower);
if (mc) return ‘Mc’ + nameUp(mc[1]) + dot;
if (NAME_MACS.indexOf(‘ ‘ + lower + ‘ ‘) >= 0) return ‘Mac’ + nameUp(lower.slice(3)) + dot;
return nameUp(lower) + dot;
}
function nameWellFormed(atom){
// “Mccoll” has its leading capital but still wants McColl. Safe only
// because “Mc” is unambiguous — “Mac” deliberately has no such rule.
if (/^Mc[a-z]{2,}$/.test(atom)) return false;
return /^(?:[A-Z][a-z]*|[A-Z][a-z]+[A-Z][A-Za-z]*|O[A-Z][a-z]+|[A-Z]{1,3}|[IVX]+)$/.test(atom);
}
function tidyName(value){
var v = (value || ”).replace(/s+/g, ‘ ‘).replace(/^ | $/g, ”);
if (!v) return value;
if (/[@d,]|&[a-z]+;|&#|https?:|www./i.test(v)) return value; // not a name
var words = v.split(‘ ‘);
var deliberate = words.every(function(w){
// A lone particle is not a particle — it is someone’s name (Di, Al).
if (words.length > 1 && isParticle(w)) return true;
return w.split(/[-‘u2019.]/).filter(function(seg){ return /[a-z]/i.test(seg); })
.every(function(seg){ return nameWellFormed(seg); });
});
if (deliberate) return v;
return words.map(function(w, i){
if (i > 0 && isParticle(w)) return w.toLowerCase();
return w.split(/([-‘u2019.])/).map(function(seg){
return /^[-‘u2019.]$/.test(seg) ? seg : nameRecase(seg);
}).join(”);
}).join(‘ ‘);
}
// ── Thousand separators ──────────────────────────────────────────────
// Opt-in per field (data-thousands). Mirrors formatThousands() and the
// caret helpers in the CRM’s packages/shared/src/numbers.ts. The CRM
// strips the separators again on submit, so storage is unaffected.
function groupThousands(value){
var raw = String(value == null ? ” : value).replace(/,/g, ”);
if (!raw) return ”;
var neg = raw.charAt(0) === ‘-‘;
var body = neg ? raw.slice(1) : raw;
var dot = body.indexOf(‘.’);
var digits = function(x){ return x.replace(/[^0-9]/g, ”); };
var whole = digits(dot === -1 ? body : body.slice(0, dot));
var frac = dot === -1 ? null : digits(body.slice(dot + 1));
if (!whole && frac === null) return neg ? ‘-‘ : ”;
var grouped = whole.replace(/B(?=(d{3})+(?!d))/g, ‘,’);
return (neg ? ‘-‘ : ”) + grouped + (frac === null ? ” : ‘.’ + frac);
}
// Reposition by digit count, not character index: commas appear and
// vanish to the left of the caret, and counting characters would throw
// it to the end of the field on every keystroke.
function caretAfterGrouping(formatted, digitsBefore){
if (digitsBefore <= 0) return 0;
var seen = 0;
for (var i = 0; i parseFloat(cmp);
case ‘lessthan': return parseFloat(val) < parseFloat(cmp);
case 'contains': return val.indexOf(cmp) !== -1;
case 'startswith': return val.indexOf(cmp) === 0;
case 'endswith': return val.slice(-cmp.length) === cmp;
default: return false; // fail closed — mirror the CRM shared matcher
}
}
function applyConditionals() {
Object.keys(condMap).forEach(function(fieldId) {
var cond = condMap[fieldId];
var rules = cond.rules || [];
var match = cond.logicType === 'any'
? rules.some(evalRule)
: rules.every(evalRule);
var show = cond.actionType === 'show' ? match : !match;
var wrapper = form.querySelector('[data-field-id="' + fieldId + '"]');
if (!wrapper) {
var el = form.querySelector('[name="' + fieldId + '"], [name="' + fieldId + '[]"]');
if (el) wrapper = el.closest('.crm-field, .crm-half');
}
if (wrapper) wrapper.style.display = show ? '' : 'none';
});
}
form.addEventListener('change', applyConditionals);
form.addEventListener('input', applyConditionals);
applyConditionals();
// ── Required-field validation (Next + Submit) ────────────────────────
// Validate from the form's field config (id + type), NOT the [required]
// HTML attribute: a checkbox group can't carry a meaningful `required`
// (native means "tick every box"), so attribute checks skip it — which is
// why empty checkbox questions slipped past Next straight to submit.
var crmRequired = [{"id":"c364951b-ecb0-4a12-bfa4-9dbe564310e8","type":"name","label":"Name"},{"id":"910107df-b8a8-4541-879f-541cef9449e4","type":"email","label":"Email"},{"id":"3b317ed9-d327-4fe2-b92e-67efede67e6d","type":"phone","label":"Phone"},{"id":"c6fae2f0-c335-452b-aa03-2cc066163d71","type":"textarea","label":"Please give us details of how we can help and the properties in question"},{"id":"34c27d9f-b3d1-4a6d-b720-24000e9fa468","type":"checkbox","label":"Privacy Policy"}];
function crmWrapper(id) {
var w = form.querySelector('[data-field-id="' + id + '"]');
if (!w) { var el = form.querySelector('[name="' + id + '"], [name="' + id + '[]"]'); if (el) w = el.closest('.crm-field, .crm-half'); }
return w;
}
function crmFilled(fld) {
// The [name^="id["] arm catches composite fields. A name renders as id[first] /
// id[last] and an address as id[street] / id[city] / …, none of which match
// the two exact-name selectors – so els came back empty, the !els.length guard
// below returned true, and the field counted as filled. With novalidate on the
// form the browser's own required check is off too, so a required Name field
// could be left completely blank and both Next and Submit went through.
var els = form.querySelectorAll('[name="' + fld.id + '"], [name="' + fld.id + '[]"], [name^="' + fld.id + '["]');
if (!els.length) return true;
// Decide on what is actually RENDERED, not the declared type. A `product`
// field is choices (radio/checkbox) for a priced list but a single amount
// box for a user-defined price — and testing that box for .checked, which
// a text input never has, meant a required product field could never be
// satisfied. An invoice or "Request For Payment" form was unsubmittable:
// the payer typed an amount, hit Submit, and got "Please select at least
// one option." under the box, every time.
var checkable = Array.prototype.filter.call(els, function (el) {
return el.type === 'checkbox' || el.type === 'radio';
});
if (checkable.length) { return checkable.some(function (el) { return el.checked; }); }
if (['checkbox','radio','consent'].indexOf(fld.type) !== -1) { return false; }
// Test the sub-inputs the renderer actually marked required – a name field puts
// `required` on both first and last, an address field on none of its six – and
// fall back to the first input when nothing is marked, which is the existing
// single-input behaviour unchanged.
var reqEls = Array.prototype.filter.call(els, function (el) { return el.hasAttribute('required'); });
// Repeaters name their sub-inputs '[][]’, so the prefix arm above
// collects EVERY rendered row. Demanding all of them be filled means one blank
// extra row – added by mistake, or rendered by settings.minRows – blocks Next and
// Submit with a generic message on the wrapper. Only the first row is required.
var firstRow = form.querySelector(‘[data-field-id=”‘ + fld.id + ‘”] .crm-repeater-row’);
if (firstRow) {
reqEls = Array.prototype.filter.call(reqEls, function (el) { return firstRow.contains(el); });
}
var toTest = reqEls.length ? reqEls : [els[0]];
return Array.prototype.every.call(toTest, function (el) { return (el.value || ”).trim() !== ”; });
}
function crmFirstInvalid(pageIdx) {
for (var i = 0; i < crmRequired.length; i++) {
var fld = crmRequired[i]; var w = crmWrapper(fld.id);
if (!w) continue;
if (w.style.display === 'none') continue;
if (pageIdx != null) { var pd = w.closest('[data-page]'); if (!pd || parseInt(pd.getAttribute('data-page')) !== pageIdx) continue; }
if (!crmFilled(fld)) return fld;
}
return null;
}
// Shows the error in a red box UNDER the field, matching the form's own
// .crm-message error styling, and scrolls to it.
function crmMarkError(w, text) {
if (!w) return;
var e = w.querySelector('.crm-inline-error');
if (!e) { e = document.createElement('div'); e.className = 'crm-inline-error'; w.appendChild(e); }
e.style.cssText = 'background:#fee2e2;color:#991b1b;padding:.55rem .75rem;border-radius:4px;margin-top:.4rem;font-size:.9rem';
e.textContent = text || 'Please answer this before continuing.';
}
function crmClearError(w) { if (!w) return; var e = w.querySelector('.crm-inline-error'); if (e) e.parentNode.removeChild(e); }
function crmReportInvalid(fld) {
var w = crmWrapper(fld.id);
// Match the message to what the visitor is actually looking at — an amount
// box should not be told to "select an option".
var els = form.querySelectorAll('[name="' + fld.id + '"], [name="' + fld.id + '[]"]');
var hasChoices = Array.prototype.some.call(els, function (el) {
return el.type === 'checkbox' || el.type === 'radio';
});
var t;
if (fld.type === 'product' && !hasChoices) { t = 'Please enter an amount.'; }
else if (['checkbox','product'].indexOf(fld.type) !== -1) { t = 'Please select at least one option.'; }
else if (fld.type === 'radio') { t = 'Please choose an option.'; }
else { t = 'Please fill this in.'; }
crmMarkError(w, t);
if (w && w.scrollIntoView) w.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
form.addEventListener('change', function(e){ var w = e.target && e.target.closest ? e.target.closest('.crm-field, .crm-half, [data-field-id]') : null; if (w) crmClearError(w); });
form.addEventListener('input', function(e){ var w = e.target && e.target.closest ? e.target.closest('.crm-field, .crm-half, [data-field-id]') : null; if (w) crmClearError(w); });
// ── Multi-page navigation ────────────────────────────────────────────
// ── Submit ────────────────────────────────────────────────────────────
var submitBtn = form.querySelector('button[type=submit]');
var btnText = submitBtn ? submitBtn.textContent : 'Submit';
var crmApiUrl = "https://crm-api.property118.com";
function collectFormData() {
var data = {};
for (var j = 0; j < form.elements.length; j++) {
var el = form.elements[j];
if (!el.name) continue;
if (el.type === 'file') continue; // handled by uploadFiles()
if (el.type === 'radio' && !el.checked) continue;
if (el.type === 'checkbox') {
if (!el.checked) continue;
var k = el.name.replace('[]','');
data[k] = data[k] ? data[k].concat([el.value]) : [el.value];
} else {
data[el.name] = el.value;
}
}
// Repeater fields: sub-inputs are named "[][]”.
// Group those flat keys into an array of row objects under the repeater
// id. Plain fields, checkbox “[]” arrays and single-bracket composites
// (name[first], address[city]) don’t match the double-bracket pattern, so
// ordinary submissions are unchanged.
var _repRe = /^(.+?)[(d+)][(.+)]$/;
var _repIds = {};
Object.keys( data ).forEach( function ( key ) {
var m = key.match( _repRe );
if ( ! m ) { return; }
var rid = m[1], row = parseInt( m[2], 10 ), sub = m[3];
_repIds[ rid ] = true;
if ( ! Array.isArray( data[ rid ] ) ) { data[ rid ] = []; }
if ( ! data[ rid ][ row ] || typeof data[ rid ][ row ] !== ‘object’ ) { data[ rid ][ row ] = {}; }
data[ rid ][ row ][ sub ] = data[ key ];
delete data[ key ];
} );
Object.keys( _repIds ).forEach( function ( rid ) {
if ( Array.isArray( data[ rid ] ) ) {
data[ rid ] = data[ rid ].filter( function ( r ) { return r && typeof r === ‘object'; } );
}
} );
// GF auto-substituted {user_agent} / {referer} on hidden fields
// at render time. We do the equivalent right before submit so
// fields whose default value carries these placeholders
// resolve to the browser’s actual values rather than being
// stored as literal “{user_agent}” / “{referer}” strings.
// The last ARTICLE the visitor read (set client-side on post views by
// P118_Article_Views). Used to attribute the enquiry to the article
// even after navigating away or with a stripped/absent referer.
var lastPost = ”;
try {
var lpm = document.cookie.match(/(?:^|;s*)p118_last_post=([^;]+)/);
if (lpm) lastPost = decodeURIComponent(lpm[1]);
} catch (e) {}
var subs = {
‘{user_agent}': navigator.userAgent || ”,
// Prefer the last article read; fall back to the (lossy) HTTP referer.
‘{referer}': lastPost || document.referrer || ”,
‘{last_post}': lastPost || ”,
‘{embed_url}': window.location.href || ”,
};
for (var name in data) {
if (!data.hasOwnProperty(name)) continue;
var v = data[name];
if (typeof v !== ‘string’) continue;
for (var tag in subs) {
if (v.indexOf(tag) !== -1) v = v.split(tag).join(subs[tag]);
}
data[name] = v;
}
// Agent/BDM attribution — read LIVE so it works even on a fully cached
// page (the browser always sees the real URL + cookie). URL ?ataid=/?cid=
// first, then the 30-day agent_id/cid cookies set by atat-tracking.php.
var _qp = new URLSearchParams(window.location.search);
var _ataid = _qp.get(‘ataid’) || (document.cookie.match(/(?:^|;s*)agent_id=([^;]+)/) || [])[1] || ”;
var _cid = _qp.get(‘cid’) || (document.cookie.match(/(?:^|;s*)cid=([^;]+)/) || [])[1] || ”;
if (_ataid) data.agent_id = decodeURIComponent(_ataid);
if (_cid) data.bdm_id = decodeURIComponent(_cid);
// Which page the form was actually on. Read live for the same reason
// as the attribution above: on a cached page the server has no idea
// which URL it is being served as, but the browser always does.
data._sourceUrl = window.location.href;
// …and what sent them to it. Read live for the same reason: a cached
// page cannot know either, but the browser does. Blank when they
// typed the address, used a bookmark, or came from an email client —
// which is the honest answer, not a gap to be filled in server-side.
data._referrerUrl = document.referrer || ”;
return data;
}
function uploadFiles(data) {
var fileInputs = form.querySelectorAll(‘input[type=file][data-crm-file-field]’);
var uploads = [];
fileInputs.forEach(function(el) {
if (!el.files || !el.files[0]) return;
var fd = new FormData();
fd.append(‘file’, el.files[0]);
var fieldName = el.name;
uploads.push(
fetch(crmApiUrl + ‘/public/forms/’ + “08ceeaae-4809-444e-ba0f-88da5836c463″ + ‘/upload’, { method: ‘POST’, body: fd })
.then(function(r) {
if (!r.ok) throw new Error(‘File upload failed (‘ + r.status + ‘)’);
return r.json();
})
.then(function(res) {
if (res.path) data[fieldName] = res.path;
else throw new Error(res.error || ‘File upload failed’);
})
);
});
return Promise.all(uploads).then(function() { return data; });
}
function submitFormData(data) {
var body = new FormData();
body.append(‘action’, ‘p118_crm_submit’);
body.append(‘form_id’, “08ceeaae-4809-444e-ba0f-88da5836c463″);
body.append(‘data’, JSON.stringify(data));
// Embed-page context for GF-style merge tags ({embed_url},
// {embed_post:post_title}, {embed_post:ID}). Captured PHP-side
// at render time, then echoed to JS so the submit fetch can
// forward to V2 as request headers.
body.append(‘embed_url’, “”);
body.append(‘embed_post_id’, “0”);
body.append(‘embed_post_title’, “”);
return fetch(“https://www.property118.com/wp-admin/admin-ajax.php”, { method: ‘POST’, body: body, credentials: ‘same-origin’ })
.then(function(r){ return r.json(); })
.then(function(res){
var p = res.data || res;
if (p && p.success) {
if (p.confirmationType === ‘form’ && p.nextFormId) {
return swapInNextForm(p.nextFormId, p.prefill || {});
}
if (p.confirmationType === ‘redirect’ && p.confirmationRedirectUrl) {
window.location.href = p.confirmationRedirectUrl;
} else {
form.style.display = ‘none';
msg.className = ‘crm-message success';
msg.innerHTML = p.confirmationMessage || ‘Thank you for your submission.';
// Carry the calculator’s own results across. A calculator form
// renders its figures into [data-iht-results] as the visitor
// types; hiding the form hid those too, so the completion
// screen went blank and the visitor lost what they had just
// worked out. Nothing happens on forms without that element.
var resultsEl = form.querySelector(‘[data-iht-results]’);
if (resultsEl && resultsEl.innerHTML.trim()) {
var carried = document.createElement(‘div’);
carried.className = ‘crm-message-results';
carried.innerHTML = resultsEl.innerHTML;
msg.appendChild(carried);
}
// Download link for a generated report, present only when the
// API filed the PDF before responding. Built through the DOM
// rather than concatenated into innerHTML so the URL cannot
// inject markup.
if (p.reportDownloadUrl) {
var dlWrap = document.createElement(‘p’);
dlWrap.className = ‘crm-report-download';
var dl = document.createElement(‘a’);
dl.className = ‘crm-report-button';
// Building through the DOM stops markup injection but not a
// javascript: URL, which would run in this origin the moment
// the submitter clicks the button.
dl.href = /^https?:///i.test(String(p.reportDownloadUrl)) ? p.reportDownloadUrl : ‘#';
dl.target = ‘_blank';
dl.rel = ‘noopener noreferrer';
dl.textContent = ‘Download your report (PDF)';
dlWrap.appendChild(dl);
msg.appendChild(dlWrap);
}
msg.style.display = ‘block';
if (msg.scrollIntoView) {
msg.scrollIntoView({ behavior: ‘smooth’, block: ‘start’ });
}
}
} else {
throw new Error((p && p.error) || ‘Submission failed.’);
}
});
}
// Replace this whole form widget with another form, rendered server-side
// with the carried-over values seeded in. Inline injected via
// innerHTML won’t run, so we re-create each script node to execute it
// (this is what wires up the new form’s submit / conditional logic).
function swapInNextForm(nextFormId, prefill) {
var rbody = new FormData();
rbody.append(‘action’, ‘p118_crm_render_form’);
rbody.append(‘form_id’, nextFormId);
rbody.append(‘prefill’, JSON.stringify(prefill || {}));
return fetch(“https://www.property118.com/wp-admin/admin-ajax.php”, { method: ‘POST’, body: rbody, credentials: ‘same-origin’ })
.then(function(r){ return r.json(); })
.then(function(res2){
var pd = res2.data || res2;
if (!pd || !pd.html) { throw new Error((pd && pd.error) || ‘Could not load the next form.’); }
var frag = document.createElement(‘div’);
frag.innerHTML = pd.html;
var parent = wrap.parentNode;
var nodes = [];
while (frag.firstChild) {
var node = frag.firstChild;
parent.insertBefore(node, wrap);
nodes.push(node);
}
parent.removeChild(wrap);
function reexec(old) {
var s = document.createElement(‘script’);
for (var a = 0; a < old.attributes.length; a++) {
s.setAttribute(old.attributes[a].name, old.attributes[a].value);
}
if (!old.src) { s.textContent = old.textContent; }
old.parentNode.replaceChild(s, old);
}
nodes.forEach(function(n){
if (n.tagName === 'SCRIPT') { reexec(n); }
else if (n.querySelectorAll) {
var scripts = n.querySelectorAll('script');
for (var k = 0; k < scripts.length; k++) { reexec(scripts[k]); }
}
});
var first = nodes[0];
try { if (first && first.scrollIntoView) first.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch (e) {}
});
}
form.addEventListener('submit', function(e){
e.preventDefault();
// Validate required fields before submitting. Next only guards the pages
// before it, so the final page (and single-page forms) are checked here;
// jump to the first page that has a problem.
var vbad0 = crmFirstInvalid(null);
if (vbad0) { crmReportInvalid(vbad0); return; }
var data = collectFormData();
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Processing…'; }
msg.style.display = 'none';
// Standard form (no payment)
uploadFiles(data)
.then(function(d) { return submitFormData(d); })
.catch(function(err){
resetTurnstile();
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = btnText; }
msg.className = 'crm-message error';
msg.textContent = err.message;
msg.style.display = 'block';
});
});
// ── Repeater fields (add / remove rows) ──────────────────────────
form.querySelectorAll( '.crm-repeater' ).forEach( function ( rep ) {
var rowsWrap = rep.querySelector( '.crm-repeater-rows' );
var tpl = rep.querySelector( '.crm-repeater-template' );
var addBtn = rep.querySelector( '.crm-repeater-add' );
if ( addBtn && tpl && rowsWrap ) {
addBtn.addEventListener( 'click', function () {
var idx = parseInt( rep.getAttribute( 'data-next-index' ) || '1', 10 );
var tmp = document.createElement( 'div' );
tmp.innerHTML = tpl.innerHTML.split( '__ROW__' ).join( idx );
var row = tmp.firstElementChild;
if ( ! row ) { return; }
row.setAttribute( 'data-row', idx );
rowsWrap.appendChild( row );
rep.setAttribute( 'data-next-index', idx + 1 );
rep.dispatchEvent( new CustomEvent( 'crm-repeater-change', { bubbles: true } ) );
} );
}
if ( rowsWrap ) {
rowsWrap.addEventListener( 'click', function ( e ) {
var btn = e.target.closest ? e.target.closest( '.crm-repeater-remove' ) : null;
if ( ! btn ) { return; }
if ( rowsWrap.querySelectorAll( '.crm-repeater-row' ).length <= 1 ) { return; }
var r = btn.closest( '.crm-repeater-row' );
if ( r ) { r.remove(); }
rep.dispatchEvent( new CustomEvent( 'crm-repeater-change', { bubbles: true } ) );
} );
}
} );
})();
The post PRS Database tipping point – Turning landlord problems into cash in the bank appeared first on Property118.
View Full Article: PRS Database tipping point – Turning landlord problems into cash in the bank
Landlords need an energy roadmap for 2030 targets
Property118

Landlords need an energy roadmap for 2030 targets
Landlords need the government to provide a clear roadmap to help prepare for the sector’s 2030 energy efficiency targets.
The warning comes from the National Residential Landlords Association (NRLA) and Energy Saving Trust.
They say that leaving investment until nearer the deadline could put installers, supply chains and council enforcement teams under pressure at the same time.
Their joint briefing calls for ministers to explain how Minimum Energy Efficiency Standards (MEES) will fit alongside changes to Energy Performance Certificates, exemptions and financial support.
With four years to prepare, the organisations say delivery depends on accessible finance, enough skilled tradespeople and reliable advice.
Landlord energy improvements
The NRLA’s chief executive, Ben Beadle, said: “Landlords want to provide homes that are warmer, cheaper to heat and fit for the future.
“Whilst the changes announced by the government earlier this year have made the proposed framework more workable, the focus now needs to be on making sure it can actually be delivered.”
He added: “2030 may sound some way off, but upgrading homes on the scale now needed requires landlords, installers, lenders, local authorities and tenants to start preparing now.
“That means providing urgent certainty about what will be required, ensuring the tradespeople and finance that landlords will need are available to carry out the work.”
Finance for improvements
The report on how to deliver the 2030 energy targets follows a parliamentary roundtable chaired by Clive Betts MP, the former Housing Select Committee chair, involving the two organisations and representatives from 10 others.
He said: “Improving the energy efficiency of the rental housing stock is a vital part of the government’s mission to address cost-of-living pressures and improve conditions across the sector.
“Ministers now need to turn welcome ambitions into reality on the ground.”
Participants welcomed the direction provided by the Warm Homes Plan and the revised approach to MEES for privately rented homes.
However, they called for the government to work with lenders and the wider sector to support early investment and ensure publicly funded improvements deliver fairer outcomes for tenants.
Advice and enforcement
Businesses also need sufficient certainty about future demand to invest in training and apprenticeships, alongside stronger checks on the quality of retrofit work.
The planned Warm Homes Agency should provide impartial advice tailored to landlords and tenants, working with existing services, the briefing says.
It also calls for simpler complaints and redress procedures, with councils being properly resourced to enforce standards.
Older and disabled tenants, along with households at risk of fuel poverty, should receive tailored support and clear communication.
The Energy Saving Trust’s Stew Horne said: “New energy efficiency standards for the private rented sector are an important step towards upgrading privately rented homes to make them warmer and more efficient for renters.
“To ensure successful delivery of these standards, it is essential that the right support is in place for both landlords and tenants.”
He added that access to green finance products, such as low-cost loans, will support landlords to invest in their properties and bring in-depth advice to help navigate the energy requirements.
The post Landlords need an energy roadmap for 2030 targets appeared first on Property118.
View Full Article: Landlords need an energy roadmap for 2030 targets
Who will protect landlords when the threats begin?
Property118

Who will protect landlords when the threats begin?
Just when you think you’ve heard it all as a landlord, there comes a flurry of stories that are both appalling and sobering.
I’m talking about the Property118 community this week discussing the threats they have received from aggrieved tenants.
Threats to burn down a landlord’s home tend to stay with you.
When the person making the threat knows where you live, providing someone with a home can suddenly feel like putting your own family at risk.
Beneath a Property118 reader’s question, landlords have described death threats, intimidation and unwanted visits to their homes.
One says threats to kill them and burn down both their properties were recorded by police.
Another describes threatening telephone calls and someone pursuing a grievance to their former family home.
The original correspondent alleges that a tenant sabotaged their car’s brakes.
These are readers’ accounts, rather than independently established findings, but they deserve something better than being dismissed as landlords having another moan.
They explain why the forthcoming PRS Database has prompted questions that ministers should answer now.
Tell us who can see what
The government’s published guidance requires individual landlords to provide their name, date of birth, residential address, telephone number and email address.
It also says: “We’ll publish a list of information that the public will be able to access at a later stage.”
‘Later’. That’s a reassuring word for someone who has already had an angry tenant on their doorstep.
Let’s be clear: this does not establish that landlords’ home addresses or dates of birth will be published.
Collecting information and displaying it publicly are different things.
But why leave landlords guessing?
What if, and it is a big if, names, home addresses, dates of birth and contact numbers become publicly accessible?
What are landlords supposed to do then?
Change their telephone number? Move house? Their date of birth is rather harder to replace.
Or will access be restricted to tenants who can demonstrate that they hold a tenancy agreement?
If so, which details will they see, and why?
A verified tenancy would establish a relationship with the landlord.
It would not establish that the person seeking access poses no threat.
The experiences described by readers should make that distinction painfully obvious.
Privacy needs more than promises
How will these arrangements comply with UK GDPR?
The question deserves a proper explanation.
That’s because data protection requires lawful, fair and transparent processing, information limited to what is necessary, and appropriate security.
A government database must meet those requirements too.
There may be a legitimate reason to collect information for identification or enforcement.
That does not automatically justify showing the same information to tenants or strangers.
Ministers should explain why each personal detail is needed, who can access it and what safeguards exist for landlords with documented histories of threats.
Even information withheld from public view needs protection against unauthorised access.
Landlords are entitled to ask about that without being accused of opposing accountability.
Tenants need reliable ways to contact landlords, serve notices and enforce their rights.
Those arrangements should also protect the safety of the person receiving them.
Tenants pay for another worry
The consequences extend beyond the registration form.
Some readers say they will sell when their current tenants leave.
Others describe already putting properties on the market after frightening experiences.
We cannot measure a national trend from a comment thread.
We can, however, recognise the decision being described: the income no longer compensates for the worry.
For landlords already weighing rising costs, greater obligations and the possibility of a difficult tenancy, uncertainty about personal security adds another reason to stop.
The next applicant then has one fewer home to consider.
Those who remain may face landlords increasingly unwilling to take a chance.
Where does this end?
Before demanding personal information, the government should explain precisely how it will protect it.
Would the Housing Minister be comfortable having their home address, date of birth and telephone number accessible through the same system? If the answer is no, ministers should understand why landlords are asking these questions.
Having something to protect is not the same as having something to hide.
Landlords should not have to wait until something goes wrong to discover that the safeguards they expected were never there.
Until next time,
The Landlord Crusader
The post Who will protect landlords when the threats begin? appeared first on Property118.
View Full Article: Who will protect landlords when the threats begin?
Tenants rate landlords positively despite public criticism
Property118

Tenants rate landlords positively despite public criticism
Private tenants describe their landlords positively despite the criticism they face in public debate, according to research from Yorkshire Building Society.
Just 5% of those renting privately described their landlord as exploitative.
The lender surveyed 4,000 adults for its policy paper, No Way Home? Restoring Britain’s Housing Ladder.
Among private tenants, 40% described their landlord as reasonable and the same proportion said theirs was approachable.
Tenants’ views of landlords
The building society’s managing director of homes, Tom Simpson, said: “Landlords are often portrayed negatively in public debate, yet our findings show a very different picture among those with direct experience of renting, demonstrating a clear disconnect between perception and reality.
“The vast majority of landlords provide homes that tenants rely on every day, and most are viewed positively by the people living in their properties.”
He added: “The conversation around housing should be balanced and evidence-based.
“Our research suggests that while perceptions of landlords can often be shaped by wider narratives, the lived experience of many renters is much more positive.”
Private landlords offer choice
Asked to describe landlords in their own words, respondents most used terms including ‘good’, ‘professional’, ‘friendly’ and ‘understanding’.
Among private tenants, 36% described their landlord as fair and 29% as responsive.
Negative assessments included being negligent, selected by 9%, unresponsive by 8% and unreasonable by 7%.
Across those surveyed, 60% agreed that private landlords are vital because they offer choice.
Support for responsible landlords
Meanwhile, 72% of respondents agreed that renting provides flexibility for people who do not wish to buy a home.
The society, which owns Accord Mortgages, also calls for more support for responsible landlords to encourage them to maintain and expand the supply of good-quality homes to rent.
The findings were released to mark the 30th anniversary of the first buy to let mortgage.
Around one in five households in England live in privately rented accommodation.
The post Tenants rate landlords positively despite public criticism appeared first on Property118.
View Full Article: Tenants rate landlords positively despite public criticism
Landlords: Are you waiting for Easter?
Property118

Landlords: Are you waiting for Easter?
We regularly speak to landlords who knew they wanted to leave the PRS or downsize after years of rising costs, increasing regulation, tenant problems, maintenance demands and diminishing returns, years before they finally spoke to us.
They wanted out but could never find the right time to leave. We know that’s still true and there are thousands more landlords putting their plans on hold – either waiting for things to improve or get so bad, the decision is made for them.
Some landlords who have held on might be thinking the biggest Renters’ Rights Act changes are already behind them. Unfortunately, they may need to think again.
The next phase begins on 15 December, when the PRS database starts rolling out across England – and 2027 will bring even more changes, including new property income tax rates of 22%, 42% and 47% and Making Tax Digital.
For landlords who have already decided they do not want to be part of the increasingly regulated PRS, that date creates a clear incentive to sell sooner rather than later.
The problem is that time is already running short.
Rightmove says it now takes an average of 216 days to find a buyer and complete a property sale. That means the landlords listing vacant property with a high street agent to sell to owner occupiers today could still be waiting at Easter.
A lot more can change between now and Easter.
With the Autumn budget on October 28th this year and 3 interest rate decisions between now and then it’s no wonder so many landlords are prioritising speed, certainty and convenience. They’re making sure they are protected from future challenges by selling to investors or property-buying companies who can complete faster and will buy with tenants in situ.
Both investors and property-buying companies value properties according to their suitability and potential yield. Where the buyer pool is small and limited to investors, open market value will often give way to yield. However, when a larger pool includes owner-occupiers, increased competition can force yield to give way to open market value.
Landlord Sales Agency offers the perfect solution: a good sales price determined by competition as well as yield, combined with a significantly faster and easier sale – without automatically having to evict your tenants first.
There is still time, but landlords who want to secure the best possible price and complete before the PRS database reaches their area need to act now.
Comprised of a team of the UK property industry’s leaders, we’ve developed a reputation for being the number one solution to go to for landlords looking to sell. We’re able to take entire portfolios off your hands, or sell individually, no matter what the tenant situation is, or the condition of the properties.
Connecting your buy-to-lets with our database of over 30,000 private buyers, a network of nationwide estate agents, local agents, and – if you need even more offers – our own private buying fund.
Our process generates a bidding war to drive up prices to ensure you’re getting the price you want. Most of our properties sell within 21 days.
We’ll also manage the entire sale for you. We know that right now landlords are more stressed than ever before. That’s no problem for us. All you have to do is get in touch, and we’ll manage the entire process for you, including negotiating with tenants, creating video tours of your properties and even funding refurbs to get you higher prices, depending on the property.
On top of that, we have a team of builders who can jump in and help and we’re happy to take on the costs – no problem, we’ll simply make those costs back on the sale.
While landlords still need to be realistic on price – it’s a tough market out there – we’re extremely confident that we can get you what you need to exit the market before the bills start mounting.
When a landlord with a portfolio of 5 tenanted properties that spanned from Staffordshire all the way up to Newcastle Upon Tyne approached us, he wanted to sell up his portfolio fast to get out, but for the best possible price. We promised him exactly that. With a mixture of marketing to our entire network, including investors and first-time buyers, we were able to drive up the prices to ensure he was happy with the sale.
The first property sold in 2 days. 10 days after that 2 more sold. By day 24 the fourth property had sold in Durham, and by day 26 the last and final small property was sold in Lancashire.
The fifth property was sold to a buyer who was so happy with the photos and the video tour we’d got from the tenant, he made an offer without even viewing the property.
The only task the landlord had done throughout the sale was make the decision to contact us to sell. We handled everything else.
It really was that simple. £500,000 made in 26 days with zero hassle, worry or stress.
The landlord was able to sit back and enjoy just over 3 weeks of relaxation knowing he had the best team in the UK solving every single challenge for him.
So get in touch today: we’ve got the team to help you get cash in the bank before Easter. It’s time to cut your losses, and start re-investing elsewhere.
(function(){var el=document.getElementById(“ts-607776b5-3f7b-431c-b5cc-fc304603fee3″);if(!el)return;var b=document.body,h=document.documentElement;var dark=b.classList.contains(“dark-mode”)||b.classList.contains(“dark”)||b.classList.contains(“night-mode”)||h.classList.contains(“dark”);if(!dark){var bg=window.getComputedStyle(b).backgroundColor,m=bg.match(/d+/g);if(m)dark=(m[0]*0.299+m[1]*0.587+m[2]*0.114)<128;}el.setAttribute("data-theme",dark?"dark":"light");if(window.turnstile&&el.childElementCount===0){try{window.turnstile.render(el,{sitekey:el.getAttribute("data-sitekey"),theme:el.getAttribute("data-theme")});}catch(e){}}})();
(function(){
var uid = “crm-form-ba09a29b”;
var form = document.getElementById(uid + ‘-form’);
var wrap = document.getElementById(uid);
var msg = wrap.querySelector(‘.crm-message’);
var totalPages = 1;
var curPage = 0;
// ── Name casing ──────────────────────────────────────────────────────
// Tidy a name box as the visitor leaves it, so they are not staring at
// “JOHN SMITH” in their own form. Deliberately cosmetic: the API runs
// the same normalisation on submit and is the authority on what gets
// stored, so a disagreement here is harmless. Kept in step with
// packages/shared/src/names.ts.
var NAME_PARTICLES = ‘ van von de del della der den di da dal du la le el bin ibn al dos das do ter ten ‘;
var NAME_MACS = ‘ macdonald macintyre macleod macarthur macgregor macneil macpherson macrae mackinnon macqueen ‘;
function nameUp(s){ return s.replace(/[a-z]/i, function(c){ return c.toUpperCase(); }); }
function isParticle(w){ return NAME_PARTICLES.indexOf(‘ ‘ + w.toLowerCase() + ‘ ‘) >= 0; }
function nameRecase(atom){
if (!/[a-z]/i.test(atom)) return atom;
var dot = atom.charAt(atom.length – 1) === ‘.’ ? ‘.’ : ”;
var core = dot ? atom.slice(0, -1) : atom;
var lower = core.toLowerCase();
if (/^(?:II|III|IV|VI|VII|VIII|IX|XI|XII)$/.test(core.toUpperCase())) return core.toUpperCase() + dot;
if (lower === ‘jr’ || lower === ‘sr’) return nameUp(lower) + dot;
if (core.length === 1) return core.toUpperCase() + dot;
var mc = /^mc([a-z]{2,})$/.exec(lower);
if (mc) return ‘Mc’ + nameUp(mc[1]) + dot;
if (NAME_MACS.indexOf(‘ ‘ + lower + ‘ ‘) >= 0) return ‘Mac’ + nameUp(lower.slice(3)) + dot;
return nameUp(lower) + dot;
}
function nameWellFormed(atom){
// “Mccoll” has its leading capital but still wants McColl. Safe only
// because “Mc” is unambiguous — “Mac” deliberately has no such rule.
if (/^Mc[a-z]{2,}$/.test(atom)) return false;
return /^(?:[A-Z][a-z]*|[A-Z][a-z]+[A-Z][A-Za-z]*|O[A-Z][a-z]+|[A-Z]{1,3}|[IVX]+)$/.test(atom);
}
function tidyName(value){
var v = (value || ”).replace(/s+/g, ‘ ‘).replace(/^ | $/g, ”);
if (!v) return value;
if (/[@d,]|&[a-z]+;|&#|https?:|www./i.test(v)) return value; // not a name
var words = v.split(‘ ‘);
var deliberate = words.every(function(w){
// A lone particle is not a particle — it is someone’s name (Di, Al).
if (words.length > 1 && isParticle(w)) return true;
return w.split(/[-‘u2019.]/).filter(function(seg){ return /[a-z]/i.test(seg); })
.every(function(seg){ return nameWellFormed(seg); });
});
if (deliberate) return v;
return words.map(function(w, i){
if (i > 0 && isParticle(w)) return w.toLowerCase();
return w.split(/([-‘u2019.])/).map(function(seg){
return /^[-‘u2019.]$/.test(seg) ? seg : nameRecase(seg);
}).join(”);
}).join(‘ ‘);
}
// ── Thousand separators ──────────────────────────────────────────────
// Opt-in per field (data-thousands). Mirrors formatThousands() and the
// caret helpers in the CRM’s packages/shared/src/numbers.ts. The CRM
// strips the separators again on submit, so storage is unaffected.
function groupThousands(value){
var raw = String(value == null ? ” : value).replace(/,/g, ”);
if (!raw) return ”;
var neg = raw.charAt(0) === ‘-‘;
var body = neg ? raw.slice(1) : raw;
var dot = body.indexOf(‘.’);
var digits = function(x){ return x.replace(/[^0-9]/g, ”); };
var whole = digits(dot === -1 ? body : body.slice(0, dot));
var frac = dot === -1 ? null : digits(body.slice(dot + 1));
if (!whole && frac === null) return neg ? ‘-‘ : ”;
var grouped = whole.replace(/B(?=(d{3})+(?!d))/g, ‘,’);
return (neg ? ‘-‘ : ”) + grouped + (frac === null ? ” : ‘.’ + frac);
}
// Reposition by digit count, not character index: commas appear and
// vanish to the left of the caret, and counting characters would throw
// it to the end of the field on every keystroke.
function caretAfterGrouping(formatted, digitsBefore){
if (digitsBefore <= 0) return 0;
var seen = 0;
for (var i = 0; i parseFloat(cmp);
case ‘lessthan': return parseFloat(val) < parseFloat(cmp);
case 'contains': return val.indexOf(cmp) !== -1;
case 'startswith': return val.indexOf(cmp) === 0;
case 'endswith': return val.slice(-cmp.length) === cmp;
default: return false; // fail closed — mirror the CRM shared matcher
}
}
function applyConditionals() {
Object.keys(condMap).forEach(function(fieldId) {
var cond = condMap[fieldId];
var rules = cond.rules || [];
var match = cond.logicType === 'any'
? rules.some(evalRule)
: rules.every(evalRule);
var show = cond.actionType === 'show' ? match : !match;
var wrapper = form.querySelector('[data-field-id="' + fieldId + '"]');
if (!wrapper) {
var el = form.querySelector('[name="' + fieldId + '"], [name="' + fieldId + '[]"]');
if (el) wrapper = el.closest('.crm-field, .crm-half');
}
if (wrapper) wrapper.style.display = show ? '' : 'none';
});
}
form.addEventListener('change', applyConditionals);
form.addEventListener('input', applyConditionals);
applyConditionals();
// ── Required-field validation (Next + Submit) ────────────────────────
// Validate from the form's field config (id + type), NOT the [required]
// HTML attribute: a checkbox group can't carry a meaningful `required`
// (native means "tick every box"), so attribute checks skip it — which is
// why empty checkbox questions slipped past Next straight to submit.
var crmRequired = [{"id":"c364951b-ecb0-4a12-bfa4-9dbe564310e8","type":"name","label":"Name"},{"id":"910107df-b8a8-4541-879f-541cef9449e4","type":"email","label":"Email"},{"id":"3b317ed9-d327-4fe2-b92e-67efede67e6d","type":"phone","label":"Phone"},{"id":"c6fae2f0-c335-452b-aa03-2cc066163d71","type":"textarea","label":"Please give us details of how we can help and the properties in question"},{"id":"34c27d9f-b3d1-4a6d-b720-24000e9fa468","type":"checkbox","label":"Privacy Policy"}];
function crmWrapper(id) {
var w = form.querySelector('[data-field-id="' + id + '"]');
if (!w) { var el = form.querySelector('[name="' + id + '"], [name="' + id + '[]"]'); if (el) w = el.closest('.crm-field, .crm-half'); }
return w;
}
function crmFilled(fld) {
// The [name^="id["] arm catches composite fields. A name renders as id[first] /
// id[last] and an address as id[street] / id[city] / …, none of which match
// the two exact-name selectors – so els came back empty, the !els.length guard
// below returned true, and the field counted as filled. With novalidate on the
// form the browser's own required check is off too, so a required Name field
// could be left completely blank and both Next and Submit went through.
var els = form.querySelectorAll('[name="' + fld.id + '"], [name="' + fld.id + '[]"], [name^="' + fld.id + '["]');
if (!els.length) return true;
// Decide on what is actually RENDERED, not the declared type. A `product`
// field is choices (radio/checkbox) for a priced list but a single amount
// box for a user-defined price — and testing that box for .checked, which
// a text input never has, meant a required product field could never be
// satisfied. An invoice or "Request For Payment" form was unsubmittable:
// the payer typed an amount, hit Submit, and got "Please select at least
// one option." under the box, every time.
var checkable = Array.prototype.filter.call(els, function (el) {
return el.type === 'checkbox' || el.type === 'radio';
});
if (checkable.length) { return checkable.some(function (el) { return el.checked; }); }
if (['checkbox','radio','consent'].indexOf(fld.type) !== -1) { return false; }
// Test the sub-inputs the renderer actually marked required – a name field puts
// `required` on both first and last, an address field on none of its six – and
// fall back to the first input when nothing is marked, which is the existing
// single-input behaviour unchanged.
var reqEls = Array.prototype.filter.call(els, function (el) { return el.hasAttribute('required'); });
// Repeaters name their sub-inputs '[][]’, so the prefix arm above
// collects EVERY rendered row. Demanding all of them be filled means one blank
// extra row – added by mistake, or rendered by settings.minRows – blocks Next and
// Submit with a generic message on the wrapper. Only the first row is required.
var firstRow = form.querySelector(‘[data-field-id=”‘ + fld.id + ‘”] .crm-repeater-row’);
if (firstRow) {
reqEls = Array.prototype.filter.call(reqEls, function (el) { return firstRow.contains(el); });
}
var toTest = reqEls.length ? reqEls : [els[0]];
return Array.prototype.every.call(toTest, function (el) { return (el.value || ”).trim() !== ”; });
}
function crmFirstInvalid(pageIdx) {
for (var i = 0; i < crmRequired.length; i++) {
var fld = crmRequired[i]; var w = crmWrapper(fld.id);
if (!w) continue;
if (w.style.display === 'none') continue;
if (pageIdx != null) { var pd = w.closest('[data-page]'); if (!pd || parseInt(pd.getAttribute('data-page')) !== pageIdx) continue; }
if (!crmFilled(fld)) return fld;
}
return null;
}
// Shows the error in a red box UNDER the field, matching the form's own
// .crm-message error styling, and scrolls to it.
function crmMarkError(w, text) {
if (!w) return;
var e = w.querySelector('.crm-inline-error');
if (!e) { e = document.createElement('div'); e.className = 'crm-inline-error'; w.appendChild(e); }
e.style.cssText = 'background:#fee2e2;color:#991b1b;padding:.55rem .75rem;border-radius:4px;margin-top:.4rem;font-size:.9rem';
e.textContent = text || 'Please answer this before continuing.';
}
function crmClearError(w) { if (!w) return; var e = w.querySelector('.crm-inline-error'); if (e) e.parentNode.removeChild(e); }
function crmReportInvalid(fld) {
var w = crmWrapper(fld.id);
// Match the message to what the visitor is actually looking at — an amount
// box should not be told to "select an option".
var els = form.querySelectorAll('[name="' + fld.id + '"], [name="' + fld.id + '[]"]');
var hasChoices = Array.prototype.some.call(els, function (el) {
return el.type === 'checkbox' || el.type === 'radio';
});
var t;
if (fld.type === 'product' && !hasChoices) { t = 'Please enter an amount.'; }
else if (['checkbox','product'].indexOf(fld.type) !== -1) { t = 'Please select at least one option.'; }
else if (fld.type === 'radio') { t = 'Please choose an option.'; }
else { t = 'Please fill this in.'; }
crmMarkError(w, t);
if (w && w.scrollIntoView) w.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
form.addEventListener('change', function(e){ var w = e.target && e.target.closest ? e.target.closest('.crm-field, .crm-half, [data-field-id]') : null; if (w) crmClearError(w); });
form.addEventListener('input', function(e){ var w = e.target && e.target.closest ? e.target.closest('.crm-field, .crm-half, [data-field-id]') : null; if (w) crmClearError(w); });
// ── Multi-page navigation ────────────────────────────────────────────
// ── Submit ────────────────────────────────────────────────────────────
var submitBtn = form.querySelector('button[type=submit]');
var btnText = submitBtn ? submitBtn.textContent : 'Submit';
var crmApiUrl = "https://crm-api.property118.com";
function collectFormData() {
var data = {};
for (var j = 0; j < form.elements.length; j++) {
var el = form.elements[j];
if (!el.name) continue;
if (el.type === 'file') continue; // handled by uploadFiles()
if (el.type === 'radio' && !el.checked) continue;
if (el.type === 'checkbox') {
if (!el.checked) continue;
var k = el.name.replace('[]','');
data[k] = data[k] ? data[k].concat([el.value]) : [el.value];
} else {
data[el.name] = el.value;
}
}
// Repeater fields: sub-inputs are named "[][]”.
// Group those flat keys into an array of row objects under the repeater
// id. Plain fields, checkbox “[]” arrays and single-bracket composites
// (name[first], address[city]) don’t match the double-bracket pattern, so
// ordinary submissions are unchanged.
var _repRe = /^(.+?)[(d+)][(.+)]$/;
var _repIds = {};
Object.keys( data ).forEach( function ( key ) {
var m = key.match( _repRe );
if ( ! m ) { return; }
var rid = m[1], row = parseInt( m[2], 10 ), sub = m[3];
_repIds[ rid ] = true;
if ( ! Array.isArray( data[ rid ] ) ) { data[ rid ] = []; }
if ( ! data[ rid ][ row ] || typeof data[ rid ][ row ] !== ‘object’ ) { data[ rid ][ row ] = {}; }
data[ rid ][ row ][ sub ] = data[ key ];
delete data[ key ];
} );
Object.keys( _repIds ).forEach( function ( rid ) {
if ( Array.isArray( data[ rid ] ) ) {
data[ rid ] = data[ rid ].filter( function ( r ) { return r && typeof r === ‘object'; } );
}
} );
// GF auto-substituted {user_agent} / {referer} on hidden fields
// at render time. We do the equivalent right before submit so
// fields whose default value carries these placeholders
// resolve to the browser’s actual values rather than being
// stored as literal “{user_agent}” / “{referer}” strings.
// The last ARTICLE the visitor read (set client-side on post views by
// P118_Article_Views). Used to attribute the enquiry to the article
// even after navigating away or with a stripped/absent referer.
var lastPost = ”;
try {
var lpm = document.cookie.match(/(?:^|;s*)p118_last_post=([^;]+)/);
if (lpm) lastPost = decodeURIComponent(lpm[1]);
} catch (e) {}
var subs = {
‘{user_agent}': navigator.userAgent || ”,
// Prefer the last article read; fall back to the (lossy) HTTP referer.
‘{referer}': lastPost || document.referrer || ”,
‘{last_post}': lastPost || ”,
‘{embed_url}': window.location.href || ”,
};
for (var name in data) {
if (!data.hasOwnProperty(name)) continue;
var v = data[name];
if (typeof v !== ‘string’) continue;
for (var tag in subs) {
if (v.indexOf(tag) !== -1) v = v.split(tag).join(subs[tag]);
}
data[name] = v;
}
// Agent/BDM attribution — read LIVE so it works even on a fully cached
// page (the browser always sees the real URL + cookie). URL ?ataid=/?cid=
// first, then the 30-day agent_id/cid cookies set by atat-tracking.php.
var _qp = new URLSearchParams(window.location.search);
var _ataid = _qp.get(‘ataid’) || (document.cookie.match(/(?:^|;s*)agent_id=([^;]+)/) || [])[1] || ”;
var _cid = _qp.get(‘cid’) || (document.cookie.match(/(?:^|;s*)cid=([^;]+)/) || [])[1] || ”;
if (_ataid) data.agent_id = decodeURIComponent(_ataid);
if (_cid) data.bdm_id = decodeURIComponent(_cid);
// Which page the form was actually on. Read live for the same reason
// as the attribution above: on a cached page the server has no idea
// which URL it is being served as, but the browser always does.
data._sourceUrl = window.location.href;
// …and what sent them to it. Read live for the same reason: a cached
// page cannot know either, but the browser does. Blank when they
// typed the address, used a bookmark, or came from an email client —
// which is the honest answer, not a gap to be filled in server-side.
data._referrerUrl = document.referrer || ”;
return data;
}
function uploadFiles(data) {
var fileInputs = form.querySelectorAll(‘input[type=file][data-crm-file-field]’);
var uploads = [];
fileInputs.forEach(function(el) {
if (!el.files || !el.files[0]) return;
var fd = new FormData();
fd.append(‘file’, el.files[0]);
var fieldName = el.name;
uploads.push(
fetch(crmApiUrl + ‘/public/forms/’ + “08ceeaae-4809-444e-ba0f-88da5836c463″ + ‘/upload’, { method: ‘POST’, body: fd })
.then(function(r) {
if (!r.ok) throw new Error(‘File upload failed (‘ + r.status + ‘)’);
return r.json();
})
.then(function(res) {
if (res.path) data[fieldName] = res.path;
else throw new Error(res.error || ‘File upload failed’);
})
);
});
return Promise.all(uploads).then(function() { return data; });
}
function submitFormData(data) {
var body = new FormData();
body.append(‘action’, ‘p118_crm_submit’);
body.append(‘form_id’, “08ceeaae-4809-444e-ba0f-88da5836c463″);
body.append(‘data’, JSON.stringify(data));
// Embed-page context for GF-style merge tags ({embed_url},
// {embed_post:post_title}, {embed_post:ID}). Captured PHP-side
// at render time, then echoed to JS so the submit fetch can
// forward to V2 as request headers.
body.append(‘embed_url’, “”);
body.append(‘embed_post_id’, “0”);
body.append(‘embed_post_title’, “”);
return fetch(“https://www.property118.com/wp-admin/admin-ajax.php”, { method: ‘POST’, body: body, credentials: ‘same-origin’ })
.then(function(r){ return r.json(); })
.then(function(res){
var p = res.data || res;
if (p && p.success) {
if (p.confirmationType === ‘form’ && p.nextFormId) {
return swapInNextForm(p.nextFormId, p.prefill || {});
}
if (p.confirmationType === ‘redirect’ && p.confirmationRedirectUrl) {
window.location.href = p.confirmationRedirectUrl;
} else {
form.style.display = ‘none';
msg.className = ‘crm-message success';
msg.innerHTML = p.confirmationMessage || ‘Thank you for your submission.';
// Carry the calculator’s own results across. A calculator form
// renders its figures into [data-iht-results] as the visitor
// types; hiding the form hid those too, so the completion
// screen went blank and the visitor lost what they had just
// worked out. Nothing happens on forms without that element.
var resultsEl = form.querySelector(‘[data-iht-results]’);
if (resultsEl && resultsEl.innerHTML.trim()) {
var carried = document.createElement(‘div’);
carried.className = ‘crm-message-results';
carried.innerHTML = resultsEl.innerHTML;
msg.appendChild(carried);
}
// Download link for a generated report, present only when the
// API filed the PDF before responding. Built through the DOM
// rather than concatenated into innerHTML so the URL cannot
// inject markup.
if (p.reportDownloadUrl) {
var dlWrap = document.createElement(‘p’);
dlWrap.className = ‘crm-report-download';
var dl = document.createElement(‘a’);
dl.className = ‘crm-report-button';
// Building through the DOM stops markup injection but not a
// javascript: URL, which would run in this origin the moment
// the submitter clicks the button.
dl.href = /^https?:///i.test(String(p.reportDownloadUrl)) ? p.reportDownloadUrl : ‘#';
dl.target = ‘_blank';
dl.rel = ‘noopener noreferrer';
dl.textContent = ‘Download your report (PDF)';
dlWrap.appendChild(dl);
msg.appendChild(dlWrap);
}
msg.style.display = ‘block';
if (msg.scrollIntoView) {
msg.scrollIntoView({ behavior: ‘smooth’, block: ‘start’ });
}
}
} else {
throw new Error((p && p.error) || ‘Submission failed.’);
}
});
}
// Replace this whole form widget with another form, rendered server-side
// with the carried-over values seeded in. Inline injected via
// innerHTML won’t run, so we re-create each script node to execute it
// (this is what wires up the new form’s submit / conditional logic).
function swapInNextForm(nextFormId, prefill) {
var rbody = new FormData();
rbody.append(‘action’, ‘p118_crm_render_form’);
rbody.append(‘form_id’, nextFormId);
rbody.append(‘prefill’, JSON.stringify(prefill || {}));
return fetch(“https://www.property118.com/wp-admin/admin-ajax.php”, { method: ‘POST’, body: rbody, credentials: ‘same-origin’ })
.then(function(r){ return r.json(); })
.then(function(res2){
var pd = res2.data || res2;
if (!pd || !pd.html) { throw new Error((pd && pd.error) || ‘Could not load the next form.’); }
var frag = document.createElement(‘div’);
frag.innerHTML = pd.html;
var parent = wrap.parentNode;
var nodes = [];
while (frag.firstChild) {
var node = frag.firstChild;
parent.insertBefore(node, wrap);
nodes.push(node);
}
parent.removeChild(wrap);
function reexec(old) {
var s = document.createElement(‘script’);
for (var a = 0; a < old.attributes.length; a++) {
s.setAttribute(old.attributes[a].name, old.attributes[a].value);
}
if (!old.src) { s.textContent = old.textContent; }
old.parentNode.replaceChild(s, old);
}
nodes.forEach(function(n){
if (n.tagName === 'SCRIPT') { reexec(n); }
else if (n.querySelectorAll) {
var scripts = n.querySelectorAll('script');
for (var k = 0; k < scripts.length; k++) { reexec(scripts[k]); }
}
});
var first = nodes[0];
try { if (first && first.scrollIntoView) first.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch (e) {}
});
}
form.addEventListener('submit', function(e){
e.preventDefault();
// Validate required fields before submitting. Next only guards the pages
// before it, so the final page (and single-page forms) are checked here;
// jump to the first page that has a problem.
var vbad0 = crmFirstInvalid(null);
if (vbad0) { crmReportInvalid(vbad0); return; }
var data = collectFormData();
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Processing…'; }
msg.style.display = 'none';
// Standard form (no payment)
uploadFiles(data)
.then(function(d) { return submitFormData(d); })
.catch(function(err){
resetTurnstile();
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = btnText; }
msg.className = 'crm-message error';
msg.textContent = err.message;
msg.style.display = 'block';
});
});
// ── Repeater fields (add / remove rows) ──────────────────────────
form.querySelectorAll( '.crm-repeater' ).forEach( function ( rep ) {
var rowsWrap = rep.querySelector( '.crm-repeater-rows' );
var tpl = rep.querySelector( '.crm-repeater-template' );
var addBtn = rep.querySelector( '.crm-repeater-add' );
if ( addBtn && tpl && rowsWrap ) {
addBtn.addEventListener( 'click', function () {
var idx = parseInt( rep.getAttribute( 'data-next-index' ) || '1', 10 );
var tmp = document.createElement( 'div' );
tmp.innerHTML = tpl.innerHTML.split( '__ROW__' ).join( idx );
var row = tmp.firstElementChild;
if ( ! row ) { return; }
row.setAttribute( 'data-row', idx );
rowsWrap.appendChild( row );
rep.setAttribute( 'data-next-index', idx + 1 );
rep.dispatchEvent( new CustomEvent( 'crm-repeater-change', { bubbles: true } ) );
} );
}
if ( rowsWrap ) {
rowsWrap.addEventListener( 'click', function ( e ) {
var btn = e.target.closest ? e.target.closest( '.crm-repeater-remove' ) : null;
if ( ! btn ) { return; }
if ( rowsWrap.querySelectorAll( '.crm-repeater-row' ).length <= 1 ) { return; }
var r = btn.closest( '.crm-repeater-row' );
if ( r ) { r.remove(); }
rep.dispatchEvent( new CustomEvent( 'crm-repeater-change', { bubbles: true } ) );
} );
}
} );
})();
The post Landlords: Are you waiting for Easter? appeared first on Property118.
View Full Article: Landlords: Are you waiting for Easter?
Categories
- Landlords (19)
- Real Estate (9)
- Renewables & Green Issues (1)
- Rental Property Investment (1)
- Tenants (21)
- Uncategorized (12,989)
Archives
- October 2026 (15)
- September 2026 (57)
- August 2026 (61)
- July 2026 (63)
- June 2026 (70)
- May 2026 (70)
- April 2026 (78)
- March 2026 (72)
- February 2026 (55)
- January 2026 (52)
- December 2025 (62)
- August 2025 (51)
- July 2025 (51)
- June 2025 (49)
- May 2025 (50)
- April 2025 (48)
- March 2025 (54)
- February 2025 (51)
- January 2025 (52)
- December 2024 (55)
- November 2024 (64)
- October 2024 (82)
- September 2024 (69)
- August 2024 (55)
- July 2024 (64)
- June 2024 (54)
- May 2024 (73)
- April 2024 (59)
- March 2024 (49)
- February 2024 (57)
- January 2024 (58)
- December 2023 (56)
- November 2023 (59)
- October 2023 (67)
- September 2023 (136)
- August 2023 (131)
- July 2023 (129)
- June 2023 (128)
- May 2023 (140)
- April 2023 (121)
- March 2023 (168)
- February 2023 (155)
- January 2023 (152)
- December 2022 (136)
- November 2022 (158)
- October 2022 (146)
- September 2022 (148)
- August 2022 (169)
- July 2022 (124)
- June 2022 (124)
- May 2022 (130)
- April 2022 (116)
- March 2022 (155)
- February 2022 (124)
- January 2022 (120)
- December 2021 (117)
- November 2021 (139)
- October 2021 (130)
- September 2021 (138)
- August 2021 (110)
- July 2021 (110)
- June 2021 (60)
- May 2021 (127)
- April 2021 (122)
- March 2021 (156)
- February 2021 (154)
- January 2021 (133)
- December 2020 (126)
- November 2020 (159)
- October 2020 (169)
- September 2020 (181)
- August 2020 (147)
- July 2020 (172)
- June 2020 (158)
- May 2020 (177)
- April 2020 (188)
- March 2020 (234)
- February 2020 (212)
- January 2020 (164)
- December 2019 (107)
- November 2019 (131)
- October 2019 (145)
- September 2019 (123)
- August 2019 (112)
- July 2019 (93)
- June 2019 (82)
- May 2019 (94)
- April 2019 (88)
- March 2019 (78)
- February 2019 (77)
- January 2019 (71)
- December 2018 (37)
- November 2018 (85)
- October 2018 (108)
- September 2018 (110)
- August 2018 (135)
- July 2018 (140)
- June 2018 (118)
- May 2018 (113)
- April 2018 (64)
- March 2018 (96)
- February 2018 (82)
- January 2018 (92)
- December 2017 (62)
- November 2017 (100)
- October 2017 (105)
- September 2017 (97)
- August 2017 (101)
- July 2017 (104)
- June 2017 (155)
- May 2017 (135)
- April 2017 (113)
- March 2017 (138)
- February 2017 (150)
- January 2017 (127)
- December 2016 (90)
- November 2016 (135)
- October 2016 (149)
- September 2016 (135)
- August 2016 (48)
- July 2016 (52)
- June 2016 (54)
- May 2016 (52)
- April 2016 (24)
- October 2014 (8)
- April 2012 (2)
- December 2011 (2)
- November 2011 (10)
- October 2011 (9)
- September 2011 (9)
- August 2011 (3)
Calendar
Recent Posts
- RESULTS of the Property118 Landlord Sentiment Survey: Q3 2026
- Government launches Awaab’s Law consultation for private rented sector
- Government fails to explain how landlord privacy will be protected under PRS Database
- Clearsprings’ asylum accommodation profits: are landlords getting a fair deal?
- Housing benefit changes will help thousands claims government

admin