����JFIF�����%%��� }!1AQa"q2���#B��R��$3br� %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz������������������������������������������������������������������������� w!1AQaq"2�B���� #3R�br� $4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz��������������������������������������������������������������������������?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|��O�������h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@��o�E��/�?��ߵE_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ ?�z�����������goڢ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?��=[�Qg�����o����Q@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y�����[����TP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,���|-��v��(���� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�������;~��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@�������?�_�����j������ (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@��o�E��/�?��ߵE_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ ?�z�����������goڢ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?��=[�Qg�����o����Q@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y�����[����TP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,���|-��v��(���� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�������;~��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@�������?�_�����j������ (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@��o�E��/�?��ߵE_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ ?�z�����������goڢ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?��=[�Qg�����o����Q@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y�����[����TP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,��������ο�O�P��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@����(���g���Y������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���V��Y|����Y����UP��@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P����,�����,��u������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j���h�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� �@���o�E��?�?����ο�U_�P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ _�z�����������g_ڪ�?��(�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (��?�/�=[�Qe�����g����U@��P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������k�w���~���v��������� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (�� (���տ�_�����:��T�~�@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@������/���?��j�?�5o�%��?��� g����U@�����&O3�����a�;�^=�wH���D��/��*� �fX�I���,������k?g_���?�5o�%��?��� g����U@�F�����������*������?�o�}��Τ~g��ʀ�#V��Y������~ο�T�j��K/� ������������z��������#;�~���A�;��� w�F�����������*���տ��_�@�o��5����EU������������u�誠��W��[�����������O��?jW���@��տ���@�o��5����EM������������v�訠�#V��Y�������������V��Zv��~����vw�~���c�Q@���,��~���kgo���?�5o�%��/��� o����Q@��o�%�>�ߤ���߳����S������?��o�%�~�ߠ�d�߳����S����g�P��j��K?� _������������[� g�D����[�;�TP7���������'Ѿ���=��;/�P��j��K?� _������������[� g�D����[�;�TP���,��~���kgo���a������۔���B{���ea�`T�+ �n%Ц �����j��K?� _������������[� g�D����[�;�TP���,��~���kgo����?���%�/�~�����#����x��c�~�q�v�t`ȫ��_'h���������'�]�;{s� Pp=N= 5���%�����ڜs�����=���J��A@�����Kp�b��}��X�����4g v+:�Բ�+60�ʩ,� @�����������I �uO�����ToUv��bgUl�cP�T?�#V��Y������������j��K?� _����������!��X��]���������TK�|4��`� ��#��P\y��aa >NgL��j��K?� _������������[� g�D����[�;�TP���,��~���kgo���o�F�����$��ہ�� ��vݞr6��S�q''*02���[� g�D����[�;�TP���,��~���kgo���?�5o�%��/��� o����Q@�F�����������*(��տ���@�o��5����EE������������v�訠��������~1�o���}G�L�������5o�%��/��� o����Q@�F�����������*(��տ���@�o��5����EE5����%�˷���r�v����y�\~���)(?0���=[� i����>��gc��N=����5o�%��/��� o����Q@�F�����������*(��W��Z�l����m#���X�wn_�j`0C6윅����5o�%��/��� o����Q@��տ��y9���gbO�G�5@�n�>���#V��Y������~ο�T��V��Y����9�gc��s�T.�?Z_��[� e�D����Y�:��UP���,������k?g_����_�=_� n�~~�rI������w�,"~ԓ�!72���)( u��#V��Y������~ο�T�j��K/� ��������������K
�����Kr_���}�De>~��Z=��pjX�n[p(�"� �a,Ub�/�×�<����;��<�����K>��o���[�:����V���,��$��ϧ�*�����5O����տ��_�@�o��5����EU5��o�%����?�ꜜm�_�;>Gbs�S�����@��տ��_�@�o��5����EU ��տ��}�~�����v?�������-��o�l��~�ȥ�v����r��B1���@��տ���A�?����ggP��c�S�`@%�*����տ��_�@�o��5����EU������������u�誠7���� O���!c�|0��ёv��4�+�X�Vx�RX3��8����K>��o���[�:���u#�x��#V��Y������~ο�T�j��K/� ������������[� e�D����Y�:��UP���,������k?g_���O��[� g�D����[�:��T��=_� k����~��k����c�;����.8����c��z��Ͽ�/��zc�o����F?Z_��[� e�D����Y�:��UP���,������k?g_���C���,�v����v�o���H������(�z���w�/�����v ��T.G��Ϡ���տ��_�@�o��5����EU������������u�誠��W��[��'����%��o���:�Cڕ�R̀���j���������?�o���[�;������g0q�?��o�%�>o�_��>�gf����~4�������������u�誠�z���7�/��o���������_��[� e�D����Y�:��UP���,������k?g_���C���,�|�����o��;�Ԟ��9�l�z��ؠ3|��O�X�~���;~�q����Z�F�����������*���տ��_�@�o��5����EU!��տ��}�~����-��G��I�T�������������u�誠�#V��Y������~ο�T�j��K/� ����������#�=_� n|���KbB�gtdM��"�ڒA#n�63�6�m�P�����,���/���gS�u����#�9��5o�%��?��� g����U@��o�%�o�_�����u��'�������?��o��� ���3��?go���|m�ڇ���-S�O��x��>���^�����7����x�]_�>�qke>���m��4��7P�Yހ��
0byt3m1n1
0byt3m1n1
Path:
/
hermes
/
bosweb
/
web
/
b1705
/
citywideiot.net
/
mark-rodgers.com
/
drupal
/
modules
/
blogapi
/
[
Home
]
File: blogapi.module
<?php // $Id: blogapi.module,v 1.115.2.5 2008/10/08 20:12:17 goba Exp $ /** * @file * Enable users to post using applications that support XML-RPC blog APIs. */ /** * Implementation of hook_help(). */ function blogapi_help($path, $arg) { switch ($path) { case 'admin/help#blogapi': $output = '<p>'. t("The Blog API module allows your site's users to access and post to their blogs from external blogging clients. External blogging clients are available for a wide range of desktop operating systems, and generally provide a feature-rich graphical environment for creating and editing posts.") .'</p>'; $output .= '<p>'. t('<a href="@ecto-link">Ecto</a>, a blogging client available for both Mac OS X and Microsoft Windows, can be used with Blog API. Blog API also supports <a href="@blogger-api">Blogger API</a>, <a href="@metaweblog-api">MetaWeblog API</a>, and most of the <a href="@movabletype-api">Movable Type API</a>. Blogging clients and other services (e.g. <a href="@flickr">Flickr\'s</a> "post to blog") that support these APIs may also be compatible.', array('@ecto-link' => url('http://infinite-sushi.com/software/ecto/'), '@blogger-api' => url('http://www.blogger.com/developers/api/1_docs/'), '@metaweblog-api' => url('http://www.xmlrpc.com/metaWeblogApi'), '@movabletype-api' => url('http://www.movabletype.org/docs/mtmanual_programmatic.html'), '@flickr' => url('http://www.flickr.com'))) .'</p>'; $output .= '<p>'. t('Select the content types available to external clients on the <a href="@blogapi-settings">Blog API settings page</a>. If supported and available, each content type will be displayed as a separate "blog" by the external client.', array('@blogapi-settings' => url('admin/settings/blogapi'))) .'</p>'; $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@blogapi">Blog API module</a>.', array('@blogapi' => url('http://drupal.org/handbook/modules/blogapi/'))) .'</p>'; return $output; } } /** * Implementation of hook_perm(). */ function blogapi_perm() { return array('administer content with blog api'); } /** * Implementation of hook_xmlrpc(). */ function blogapi_xmlrpc() { return array( array( 'blogger.getUsersBlogs', 'blogapi_blogger_get_users_blogs', array('array', 'string', 'string', 'string'), t('Returns a list of blogs to which an author has posting privileges.')), array( 'blogger.getUserInfo', 'blogapi_blogger_get_user_info', array('struct', 'string', 'string', 'string'), t('Returns information about an author in the system.')), array( 'blogger.newPost', 'blogapi_blogger_new_post', array('string', 'string', 'string', 'string', 'string', 'string', 'boolean'), t('Creates a new post, and optionally publishes it.')), array( 'blogger.editPost', 'blogapi_blogger_edit_post', array('boolean', 'string', 'string', 'string', 'string', 'string', 'boolean'), t('Updates the information about an existing post.')), array( 'blogger.getPost', 'blogapi_blogger_get_post', array('struct', 'string', 'string', 'string', 'string'), t('Returns information about a specific post.')), array( 'blogger.deletePost', 'blogapi_blogger_delete_post', array('boolean', 'string', 'string', 'string', 'string', 'boolean'), t('Deletes a post.')), array( 'blogger.getRecentPosts', 'blogapi_blogger_get_recent_posts', array('array', 'string', 'string', 'string', 'string', 'int'), t('Returns a list of the most recent posts in the system.')), array( 'metaWeblog.newPost', 'blogapi_metaweblog_new_post', array('string', 'string', 'string', 'string', 'struct', 'boolean'), t('Creates a new post, and optionally publishes it.')), array( 'metaWeblog.editPost', 'blogapi_metaweblog_edit_post', array('boolean', 'string', 'string', 'string', 'struct', 'boolean'), t('Updates information about an existing post.')), array( 'metaWeblog.getPost', 'blogapi_metaweblog_get_post', array('struct', 'string', 'string', 'string'), t('Returns information about a specific post.')), array( 'metaWeblog.newMediaObject', 'blogapi_metaweblog_new_media_object', array('string', 'string', 'string', 'string', 'struct'), t('Uploads a file to your webserver.')), array( 'metaWeblog.getCategories', 'blogapi_metaweblog_get_category_list', array('struct', 'string', 'string', 'string'), t('Returns a list of all categories to which the post is assigned.')), array( 'metaWeblog.getRecentPosts', 'blogapi_metaweblog_get_recent_posts', array('array', 'string', 'string', 'string', 'int'), t('Returns a list of the most recent posts in the system.')), array( 'mt.getRecentPostTitles', 'blogapi_mt_get_recent_post_titles', array('array', 'string', 'string', 'string', 'int'), t('Returns a bandwidth-friendly list of the most recent posts in the system.')), array( 'mt.getCategoryList', 'blogapi_mt_get_category_list', array('array', 'string', 'string', 'string'), t('Returns a list of all categories defined in the blog.')), array( 'mt.getPostCategories', 'blogapi_mt_get_post_categories', array('array', 'string', 'string', 'string'), t('Returns a list of all categories to which the post is assigned.')), array( 'mt.setPostCategories', 'blogapi_mt_set_post_categories', array('boolean', 'string', 'string', 'string', 'array'), t('Sets the categories for a post.')), array( 'mt.supportedMethods', 'xmlrpc_server_list_methods', array('array'), t('Retrieve information about the XML-RPC methods supported by the server.')), array( 'mt.supportedTextFilters', 'blogapi_mt_supported_text_filters', array('array'), t('Retrieve information about the text formatting plugins supported by the server.')), array( 'mt.publishPost', 'blogapi_mt_publish_post', array('boolean', 'string', 'string', 'string'), t('Publish (rebuild) all of the static files related to an entry from your blog. Equivalent to saving an entry in the system (but without the ping).'))); } /** * Blogging API callback. Finds the URL of a user's blog. */ function blogapi_blogger_get_users_blogs($appid, $username, $password) { $user = blogapi_validate_user($username, $password); if ($user->uid) { $types = _blogapi_get_node_types(); $structs = array(); foreach ($types as $type) { $structs[] = array('url' => url('blog/'. $user->uid, array('absolute' => TRUE)), 'blogid' => $type, 'blogName' => $user->name .": ". $type); } return $structs; } else { return blogapi_error($user); } } /** * Blogging API callback. Returns profile information about a user. */ function blogapi_blogger_get_user_info($appkey, $username, $password) { $user = blogapi_validate_user($username, $password); if ($user->uid) { $name = explode(' ', $user->realname ? $user->realname : $user->name, 2); return array( 'userid' => $user->uid, 'lastname' => $name[1], 'firstname' => $name[0], 'nickname' => $user->name, 'email' => $user->mail, 'url' => url('blog/'. $user->uid, array('absolute' => TRUE))); } else { return blogapi_error($user); } } /** * Blogging API callback. Inserts a new blog post as a node. */ function blogapi_blogger_new_post($appkey, $blogid, $username, $password, $content, $publish) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { // Return an error if not configured type. return $error; } $edit = array(); $edit['type'] = $blogid; // get the node type defaults $node_type_default = variable_get('node_options_'. $edit['type'], array('status', 'promote')); $edit['uid'] = $user->uid; $edit['name'] = $user->name; $edit['promote'] = in_array('promote', $node_type_default); $edit['comment'] = variable_get('comment_'. $edit['type'], 2); $edit['revision'] = in_array('revision', $node_type_default); $edit['format'] = FILTER_FORMAT_DEFAULT; $edit['status'] = $publish; // check for bloggerAPI vs. metaWeblogAPI if (is_array($content)) { $edit['title'] = $content['title']; $edit['body'] = $content['description']; _blogapi_mt_extra($edit, $content); } else { $edit['title'] = blogapi_blogger_title($content); $edit['body'] = $content; } if (!node_access('create', $edit['type'])) { return blogapi_error(t('You do not have permission to create this type of post.')); } if (user_access('administer nodes') && !isset($edit['date'])) { $edit['date'] = format_date(time(), 'custom', 'Y-m-d H:i:s O'); } node_invoke_nodeapi($edit, 'blogapi new'); $valid = blogapi_status_error_check($edit, $publish); if ($valid !== TRUE) { return $valid; } node_validate($edit); if ($errors = form_get_errors()) { return blogapi_error(implode("\n", $errors)); } $node = node_submit($edit); node_save($node); if ($node->nid) { watchdog('content', '@type: added %title using blog API.', array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid")); // blogger.newPost returns a string so we cast the nid to a string by putting it in double quotes: return "$node->nid"; } return blogapi_error(t('Error storing post.')); } /** * Blogging API callback. Modifies the specified blog node. */ function blogapi_blogger_edit_post($appkey, $postid, $username, $password, $content, $publish) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $node = node_load($postid); if (!$node) { return blogapi_error(t('n/a')); } // Let the teaser be re-generated. unset($node->teaser); if (!node_access('update', $node)) { return blogapi_error(t('You do not have permission to update this post.')); } // Save the original status for validation of permissions. $original_status = $node->status; $node->status = $publish; // check for bloggerAPI vs. metaWeblogAPI if (is_array($content)) { $node->title = $content['title']; $node->body = $content['description']; _blogapi_mt_extra($node, $content); } else { $node->title = blogapi_blogger_title($content); $node->body = $content; } node_invoke_nodeapi($node, 'blogapi edit'); $valid = blogapi_status_error_check($node, $original_status); if ($valid !== TRUE) { return $valid; } node_validate($node); if ($errors = form_get_errors()) { return blogapi_error(implode("\n", $errors)); } if (user_access('administer nodes') && !isset($edit['date'])) { $node->date = format_date($node->created, 'custom', 'Y-m-d H:i:s O'); } $node = node_submit($node); node_save($node); if ($node->nid) { watchdog('content', '@type: updated %title using Blog API.', array('@type' => $node->type, '%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid")); return TRUE; } return blogapi_error(t('Error storing post.')); } /** * Blogging API callback. Returns a specified blog node. */ function blogapi_blogger_get_post($appkey, $postid, $username, $password) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $node = node_load($postid); return _blogapi_get_post($node, TRUE); } /** * Check that the user has permission to save the node with the chosen status. * * @return * TRUE if no error, or the blogapi_error(). */ function blogapi_status_error_check($node, $original_status) { $node = (object) $node; $node_type_default = variable_get('node_options_'. $node->type, array('status', 'promote')); // If we don't have the 'administer nodes' permission and the status is // changing or for a new node the status is not the content type's default, // then return an error. if (!user_access('administer nodes') && (($node->status != $original_status) || (empty($node->nid) && $node->status != in_array('status', $node_type_default)))) { if ($node->status) { return blogapi_error(t('You do not have permission to publish this type of post. Please save it as a draft instead.')); } else { return blogapi_error(t('You do not have permission to save this post as a draft. Please publish it instead.')); } } return TRUE; } /** * Blogging API callback. Removes the specified blog node. */ function blogapi_blogger_delete_post($appkey, $postid, $username, $password, $publish) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } node_delete($postid); return TRUE; } /** * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE * <a href="http://movabletype.org/docs/mtmanual_programmatic.html#item_mt%2EgetRecentPostTitles"> * returns a bandwidth-friendly list</a>. */ function blogapi_blogger_get_recent_posts($appkey, $blogid, $username, $password, $number_of_posts, $bodies = TRUE) { // Remove unused appkey (from bloggerAPI). $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { // Return an error if not configured type. return $error; } if ($bodies) { $result = db_query_range("SELECT n.nid, n.title, r.body, r.format, n.comment, n.created, u.name FROM {node} n, {node_revisions} r, {users} u WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $blogid, $user->uid, 0, $number_of_posts); } else { $result = db_query_range("SELECT n.nid, n.title, n.created, u.name FROM {node} n, {users} u WHERE n.uid = u.uid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $blogid, $user->uid, 0, $number_of_posts); } $blogs = array(); while ($blog = db_fetch_object($result)) { $blogs[] = _blogapi_get_post($blog, $bodies); } return $blogs; } function blogapi_metaweblog_new_post($blogid, $username, $password, $content, $publish) { return blogapi_blogger_new_post('0123456789ABCDEF', $blogid, $username, $password, $content, $publish); } function blogapi_metaweblog_edit_post($postid, $username, $password, $content, $publish) { return blogapi_blogger_edit_post('0123456789ABCDEF', $postid, $username, $password, $content, $publish); } function blogapi_metaweblog_get_post($postid, $username, $password) { return blogapi_blogger_get_post('01234567890ABCDEF', $postid, $username, $password); } /** * Blogging API callback. Inserts a file into Drupal. */ function blogapi_metaweblog_new_media_object($blogid, $username, $password, $file) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $usersize = 0; $uploadsize = 0; $roles = array_intersect(user_roles(FALSE, 'administer content with blog api'), $user->roles); foreach ($roles as $rid => $name) { $extensions .= ' '. strtolower(variable_get("blogapi_extensions_$rid", variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'))); $usersize= max($usersize, variable_get("blogapi_usersize_$rid", variable_get('blogapi_usersize_default', 1)) * 1024 * 1024); $uploadsize = max($uploadsize, variable_get("blogapi_uploadsize_$rid", variable_get('blogapi_uploadsize_default', 1)) * 1024 * 1024); } $filesize = strlen($file['bits']); if ($filesize > $uploadsize) { return blogapi_error(t('It is not possible to upload the file, because it exceeded the maximum filesize of @maxsize.', array('@maxsize' => format_size($uploadsize)))); } if (_blogapi_space_used($user->uid) + $filesize > $usersize) { return blogapi_error(t('The file can not be attached to this post, because the disk quota of @quota has been reached.', array('@quota' => format_size($usersize)))); } // Only allow files with whitelisted extensions and convert remaining dots to // underscores to prevent attacks via non-terminal executable extensions with // files such as exploit.php.jpg. $whitelist = array_unique(explode(' ', trim($extensions))); $name = basename($file['name']); if ($extension_position = strrpos($name, '.')) { $filename = drupal_substr($name, 0, $extension_position); $final_extension = drupal_substr($name, $extension_position + 1); if (!in_array(strtolower($final_extension), $whitelist)) { return blogapi_error(t('It is not possible to upload the file, because it is only possible to upload files with the following extensions: @extensions', array('@extensions' => implode(' ', $whitelist)))); } $filename = str_replace('.', '_', $filename); $filename .= '.'. $final_extension; } $data = $file['bits']; if (!$data) { return blogapi_error(t('No file sent.')); } if (!$file = file_save_data($data, $filename)) { return blogapi_error(t('Error storing file.')); } $row = new stdClass(); $row->uid = $user->uid; $row->filepath = $file; $row->filesize = $filesize; drupal_write_record('blogapi_files', $row); // Return the successful result. return array('url' => file_create_url($file), 'struct'); } /** * Blogging API callback. Returns a list of the taxonomy terms that can be * associated with a blog node. */ function blogapi_metaweblog_get_category_list($blogid, $username, $password) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } if (($error = _blogapi_validate_blogid($blogid)) !== TRUE) { // Return an error if not configured type. return $error; } $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $blogid, 'vid'); $categories = array(); if ($vocabularies) { foreach ($vocabularies as $vocabulary) { $terms = module_invoke('taxonomy', 'get_tree', $vocabulary->vid, 0, -1); foreach ($terms as $term) { $term_name = $term->name; foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name .'/'. $term_name; } $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid); } } } return $categories; } function blogapi_metaweblog_get_recent_posts($blogid, $username, $password, $number_of_posts) { return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, TRUE); } function blogapi_mt_get_recent_post_titles($blogid, $username, $password, $number_of_posts) { return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, FALSE); } function blogapi_mt_get_category_list($blogid, $username, $password) { return blogapi_metaweblog_get_category_list($blogid, $username, $password); } /** * Blogging API callback. Returns a list of the taxonomy terms that are * assigned to a particular node. */ function blogapi_mt_get_post_categories($postid, $username, $password) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $node = node_load($postid); $terms = module_invoke('taxonomy', 'node_get_terms', $node, 'tid'); $categories = array(); foreach ($terms as $term) { $term_name = $term->name; foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name .'/'. $term_name; } $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid, 'isPrimary' => TRUE); } return $categories; } /** * Blogging API callback. Assigns taxonomy terms to a particular node. */ function blogapi_mt_set_post_categories($postid, $username, $password, $categories) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $node = node_load($postid); $node->taxonomy = array(); foreach ($categories as $category) { $node->taxonomy[] = $category['categoryId']; } $validated = blogapi_mt_validate_terms($node); if ($validated !== TRUE) { return $validated; } node_save($node); return TRUE; } /** * Blogging API helper - find allowed taxonomy terms for a node type. */ function blogapi_mt_validate_terms($node) { // We do a lot of heavy lifting here since taxonomy module doesn't have a // stand-alone validation function. if (module_exists('taxonomy')) { $found_terms = array(); if (!empty($node->taxonomy)) { $term_list = array_unique($node->taxonomy); $params = $term_list; $params[] = $node->type; $result = db_query(db_rewrite_sql("SELECT t.tid, t.vid FROM {term_data} t INNER JOIN {vocabulary_node_types} n ON t.vid = n.vid WHERE t.tid IN (". db_placeholders($term_list) .") AND n.type = '%s'", 't', 'tid'), $params); $found_terms = array(); $found_count = 0; while ($term = db_fetch_object($result)) { $found_terms[$term->vid][$term->tid] = $term->tid; $found_count++; } // If the counts don't match, some terms are invalid or not accessible to this user. if (count($term_list) != $found_count) { return blogapi_error(t('Invalid categories submitted.')); } } // Look up all the vocabularies for this node type. $result2 = db_query(db_rewrite_sql("SELECT v.vid, v.name, v.required, v.multiple FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s'", 'v', 'vid'), $node->type); // Check each vocabulary associated with this node type. while ($vocabulary = db_fetch_object($result2)) { // Required vocabularies must have at least one term. if ($vocabulary->required && empty($found_terms[$vocabulary->vid])) { return blogapi_error(t('A category from the @vocabulary_name vocabulary is required.', array('@vocabulary_name' => $vocabulary->name))); } // Vocabularies that don't allow multiple terms may have at most one. if (!($vocabulary->multiple) && (isset($found_terms[$vocabulary->vid]) && count($found_terms[$vocabulary->vid]) > 1)) { return blogapi_error(t('You may only choose one category from the @vocabulary_name vocabulary.'), array('@vocabulary_name' => $vocabulary->name)); } } } elseif (!empty($node->taxonomy)) { return blogapi_error(t('Error saving categories. This feature is not available.')); } return TRUE; } /** * Blogging API callback. Sends a list of available input formats. */ function blogapi_mt_supported_text_filters() { // NOTE: we're only using anonymous' formats because the MT spec // does not allow for per-user formats. $formats = filter_formats(); $filters = array(); foreach ($formats as $format) { $filter['key'] = $format->format; $filter['label'] = $format->name; $filters[] = $filter; } return $filters; } /** * Blogging API callback. Publishes the given node */ function blogapi_mt_publish_post($postid, $username, $password) { $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $node = node_load($postid); if (!$node) { return blogapi_error(t('Invalid post.')); } // Nothing needs to be done if already published. if ($node->status) { return; } if (!node_access('update', $node) || !user_access('administer nodes')) { return blogapi_error(t('You do not have permission to update this post.')); } $node->status = 1; node_save($node); return TRUE; } /** * Prepare an error message for returning to the XMLRPC caller. */ function blogapi_error($message) { static $xmlrpcusererr; if (!is_array($message)) { $message = array($message); } $message = implode(' ', $message); return xmlrpc_error($xmlrpcusererr + 1, strip_tags($message)); } /** * Ensure that the given user has permission to edit a blog. */ function blogapi_validate_user($username, $password) { global $user; $user = user_authenticate(array('name' => $username, 'pass' => $password)); if ($user->uid) { if (user_access('administer content with blog api', $user)) { return $user; } else { return t('You do not have permission to edit this blog.'); } } else { return t('Wrong username or password.'); } } /** * For the blogger API, extract the node title from the contents field. */ function blogapi_blogger_title(&$contents) { if (eregi('<title>([^<]*)</title>', $contents, $title)) { $title = strip_tags($title[0]); $contents = ereg_replace('<title>[^<]*</title>', '', $contents); } else { list($title, $contents) = explode("\n", $contents, 2); } return $title; } function blogapi_admin_settings() { $node_types = array_map('check_plain', node_get_types('names')); $defaults = isset($node_types['blog']) ? array('blog' => 1) : array(); $form['blogapi_node_types'] = array( '#type' => 'checkboxes', '#title' => t('Enable for external blogging clients'), '#required' => TRUE, '#default_value' => variable_get('blogapi_node_types', $defaults), '#options' => $node_types, '#description' => t('Select the content types available to external blogging clients via Blog API. If supported, each enabled content type will be displayed as a separate "blog" by the external client.') ); $blogapi_extensions_default = variable_get('blogapi_extensions_default', 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp'); $blogapi_uploadsize_default = variable_get('blogapi_uploadsize_default', 1); $blogapi_usersize_default = variable_get('blogapi_usersize_default', 1); $form['settings_general'] = array( '#type' => 'fieldset', '#title' => t('File settings'), '#collapsible' => TRUE, ); $form['settings_general']['blogapi_extensions_default'] = array( '#type' => 'textfield', '#title' => t('Default permitted file extensions'), '#default_value' => $blogapi_extensions_default, '#maxlength' => 255, '#description' => t('Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.'), ); $form['settings_general']['blogapi_uploadsize_default'] = array( '#type' => 'textfield', '#title' => t('Default maximum file size per upload'), '#default_value' => $blogapi_uploadsize_default, '#size' => 5, '#maxlength' => 5, '#description' => t('The default maximum file size a user can upload.'), '#field_suffix' => t('MB') ); $form['settings_general']['blogapi_usersize_default'] = array( '#type' => 'textfield', '#title' => t('Default total file size per user'), '#default_value' => $blogapi_usersize_default, '#size' => 5, '#maxlength' => 5, '#description' => t('The default maximum size of all files a user can have on the site.'), '#field_suffix' => t('MB') ); $form['settings_general']['upload_max_size'] = array('#value' => '<p>'. t('Your PHP settings limit the maximum file size per upload to %size.', array('%size' => format_size(file_upload_max_size()))).'</p>'); $roles = user_roles(0, 'administer content with blog api'); $form['roles'] = array('#type' => 'value', '#value' => $roles); foreach ($roles as $rid => $role) { $form['settings_role_'. $rid] = array( '#type' => 'fieldset', '#title' => t('Settings for @role', array('@role' => $role)), '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['settings_role_'. $rid]['blogapi_extensions_'. $rid] = array( '#type' => 'textfield', '#title' => t('Permitted file extensions'), '#default_value' => variable_get('blogapi_extensions_'. $rid, $blogapi_extensions_default), '#maxlength' => 255, '#description' => t('Extensions that users in this role can upload. Separate extensions with a space and do not include the leading dot.'), ); $form['settings_role_'. $rid]['blogapi_uploadsize_'. $rid] = array( '#type' => 'textfield', '#title' => t('Maximum file size per upload'), '#default_value' => variable_get('blogapi_uploadsize_'. $rid, $blogapi_uploadsize_default), '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of a file a user can upload (in megabytes).'), ); $form['settings_role_'. $rid]['blogapi_usersize_'. $rid] = array( '#type' => 'textfield', '#title' => t('Total file size per user'), '#default_value' => variable_get('blogapi_usersize_'. $rid, $blogapi_usersize_default), '#size' => 5, '#maxlength' => 5, '#description' => t('The maximum size of all files a user can have on the site (in megabytes).'), ); } return system_settings_form($form); } function blogapi_menu() { $items['blogapi/rsd'] = array( 'title' => 'RSD', 'page callback' => 'blogapi_rsd', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); $items['admin/settings/blogapi'] = array( 'title' => 'Blog API', 'description' => 'Configure the content types available to external blogging clients.', 'page callback' => 'drupal_get_form', 'page arguments' => array('blogapi_admin_settings'), 'access arguments' => array('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); return $items; } function blogapi_init() { if (drupal_is_front_page()) { drupal_add_link(array('rel' => 'EditURI', 'type' => 'application/rsd+xml', 'title' => t('RSD'), 'href' => url('blogapi/rsd', array('absolute' => TRUE)))); } } function blogapi_rsd() { global $base_url; $xmlrpc = $base_url .'/xmlrpc.php'; $base = url('', array('absolute' => TRUE)); $blogid = 1; # until we figure out how to handle multiple bloggers drupal_set_header('Content-Type: application/rsd+xml; charset=utf-8'); print <<<__RSD__ <?xml version="1.0"?> <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd"> <service> <engineName>Drupal</engineName> <engineLink>http://drupal.org/</engineLink> <homePageLink>$base</homePageLink> <apis> <api name="MetaWeblog" preferred="false" apiLink="$xmlrpc" blogID="$blogid" /> <api name="Blogger" preferred="false" apiLink="$xmlrpc" blogID="$blogid" /> <api name="MovableType" preferred="true" apiLink="$xmlrpc" blogID="$blogid" /> </apis> </service> </rsd> __RSD__; } /** * Handles extra information sent by clients according to MovableType's spec. */ function _blogapi_mt_extra(&$node, $struct) { if (is_array($node)) { $was_array = TRUE; $node = (object)$node; } // mt_allow_comments if (array_key_exists('mt_allow_comments', $struct)) { switch ($struct['mt_allow_comments']) { case 0: $node->comment = COMMENT_NODE_DISABLED; break; case 1: $node->comment = COMMENT_NODE_READ_WRITE; break; case 2: $node->comment = COMMENT_NODE_READ_ONLY; break; } } // merge the 3 body sections (description, mt_excerpt, mt_text_more) into // one body if ($struct['mt_excerpt']) { $node->body = $struct['mt_excerpt'] .'<!--break-->'. $node->body; } if ($struct['mt_text_more']) { $node->body = $node->body .'<!--extended-->'. $struct['mt_text_more']; } // mt_convert_breaks if ($struct['mt_convert_breaks']) { $node->format = $struct['mt_convert_breaks']; } // dateCreated if ($struct['dateCreated']) { $node->date = format_date(mktime($struct['dateCreated']->hour, $struct['dateCreated']->minute, $struct['dateCreated']->second, $struct['dateCreated']->month, $struct['dateCreated']->day, $struct['dateCreated']->year), 'custom', 'Y-m-d H:i:s O'); } if ($was_array) { $node = (array)$node; } } function _blogapi_get_post($node, $bodies = TRUE) { $xmlrpcval = array( 'userid' => $node->name, 'dateCreated' => xmlrpc_date($node->created), 'title' => $node->title, 'postid' => $node->nid, 'link' => url('node/'. $node->nid, array('absolute' => TRUE)), 'permaLink' => url('node/'. $node->nid, array('absolute' => TRUE)), ); if ($bodies) { if ($node->comment == 1) { $comment = 2; } else if ($node->comment == 2) { $comment = 1; } $xmlrpcval['content'] = "<title>$node->title</title>$node->body"; $xmlrpcval['description'] = $node->body; // Add MT specific fields $xmlrpcval['mt_allow_comments'] = (int) $comment; $xmlrpcval['mt_convert_breaks'] = $node->format; } return $xmlrpcval; } /** * Validate blog ID, which maps to a content type in Drupal. * * Only content types configured to work with Blog API are supported. * * @return * TRUE if the content type is supported and the user has permission * to post, or a blogapi_error() XML construct otherwise. */ function _blogapi_validate_blogid($blogid) { $types = _blogapi_get_node_types(); if (in_array($blogid, $types, TRUE)) { return TRUE; } return blogapi_error(t("Blog API module is not configured to support the %type content type, or you don't have sufficient permissions to post this type of content.", array('%type' => $blogid))); } function _blogapi_get_node_types() { $available_types = array_keys(array_filter(variable_get('blogapi_node_types', array('blog' => 1)))); $types = array(); foreach (node_get_types() as $type => $name) { if (node_access('create', $type) && in_array($type, $available_types)) { $types[] = $type; } } return $types; } function _blogapi_space_used($uid) { return db_result(db_query('SELECT SUM(filesize) FROM {blogapi_files} f WHERE f.uid = %d', $uid)); }
© 2017 -
ZeroByte.ID
.