Best Python code snippet using localstack_python
test_contentmanager.py
Source:test_contentmanager.py
...21 cm.add_get_handler(key, foo_getter)22 m = self._make_message()23 m['Content-Type'] = 'text/plain'24 m['X-Bar-Header'] = 'foo'25 self.assertEqual(cm.get_content(m, foo='bar'), ('bar', 'foo'))26 def get_key_as_get_content_key_order(self, order, key):27 def bar_getter(msg):28 return msg['X-Bar-Header']29 def foo_getter(msg):30 return msg['X-Foo-Header']31 cm = ContentManager()32 cm.add_get_handler(key, foo_getter)33 for precedence, key in self.get_key_params.values():34 if precedence > order:35 cm.add_get_handler(key, bar_getter)36 m = self._make_message()37 m['Content-Type'] = 'text/plain'38 m['X-Bar-Header'] = 'bar'39 m['X-Foo-Header'] = 'foo'40 self.assertEqual(cm.get_content(m), ('foo'))41 def test_get_content_raises_if_unknown_mimetype_and_no_default(self):42 cm = ContentManager()43 m = self._make_message()44 m['Content-Type'] = 'text/plain'45 with self.assertRaisesRegex(KeyError, 'text/plain'):46 cm.get_content(m)47 class BaseThing(str):48 pass49 baseobject_full_path = __name__ + '.' + 'TestContentManager.BaseThing'50 class Thing(BaseThing):51 pass52 testobject_full_path = __name__ + '.' + 'TestContentManager.Thing'53 set_key_params = {54 'type': (0, Thing,),55 'full_path': (1, testobject_full_path,),56 'qualname': (2, 'TestContentManager.Thing',),57 'name': (3, 'Thing',),58 'base_type': (4, BaseThing,),59 'base_full_path': (5, baseobject_full_path,),60 'base_qualname': (6, 'TestContentManager.BaseThing',),61 'base_name': (7, 'BaseThing',),62 'str_type': (8, str,),63 'str_full_path': (9, 'builtins.str',),64 'str_name': (10, 'str',), # str name and qualname are the same65 'null_key': (11, None,),66 }67 def set_key_as_set_content_key(self, order, key):68 def foo_setter(msg, obj, foo=None):69 msg['X-Foo-Header'] = foo70 msg.set_payload(obj)71 cm = ContentManager()72 cm.add_set_handler(key, foo_setter)73 m = self._make_message()74 msg_obj = self.Thing()75 cm.set_content(m, msg_obj, foo='bar')76 self.assertEqual(m['X-Foo-Header'], 'bar')77 self.assertEqual(m.get_payload(), msg_obj)78 def set_key_as_set_content_key_order(self, order, key):79 def foo_setter(msg, obj):80 msg['X-FooBar-Header'] = 'foo'81 msg.set_payload(obj)82 def bar_setter(msg, obj):83 msg['X-FooBar-Header'] = 'bar'84 cm = ContentManager()85 cm.add_set_handler(key, foo_setter)86 for precedence, key in self.get_key_params.values():87 if precedence > order:88 cm.add_set_handler(key, bar_setter)89 m = self._make_message()90 msg_obj = self.Thing()91 cm.set_content(m, msg_obj)92 self.assertEqual(m['X-FooBar-Header'], 'foo')93 self.assertEqual(m.get_payload(), msg_obj)94 def test_set_content_raises_if_unknown_type_and_no_default(self):95 cm = ContentManager()96 m = self._make_message()97 msg_obj = self.Thing()98 with self.assertRaisesRegex(KeyError, self.testobject_full_path):99 cm.set_content(m, msg_obj)100 def test_set_content_raises_if_called_on_multipart(self):101 cm = ContentManager()102 m = self._make_message()103 m['Content-Type'] = 'multipart/foo'104 with self.assertRaises(TypeError):105 cm.set_content(m, 'test')106 def test_set_content_calls_clear_content(self):107 m = self._make_message()108 m['Content-Foo'] = 'bar'109 m['Content-Type'] = 'text/html'110 m['To'] = 'test'111 m.set_payload('abc')112 cm = ContentManager()113 cm.add_set_handler(str, lambda *args, **kw: None)114 m.set_content('xyz', content_manager=cm)115 self.assertIsNone(m['Content-Foo'])116 self.assertIsNone(m['Content-Type'])117 self.assertEqual(m['To'], 'test')118 self.assertIsNone(m.get_payload())119@parameterize120class TestRawDataManager(TestEmailBase):121 # Note: these tests are dependent on the order in which headers are added122 # to the message objects by the code. There's no defined ordering in123 # RFC5322/MIME, so this makes the tests more fragile than the standards124 # require. However, if the header order changes it is best to understand125 # *why*, and make sure it isn't a subtle bug in whatever change was126 # applied.127 policy = policy.default.clone(max_line_length=60,128 content_manager=raw_data_manager)129 message = EmailMessage130 def test_get_text_plain(self):131 m = self._str_msg(textwrap.dedent("""\132 Content-Type: text/plain133 Basic text.134 """))135 self.assertEqual(raw_data_manager.get_content(m), "Basic text.\n")136 def test_get_text_html(self):137 m = self._str_msg(textwrap.dedent("""\138 Content-Type: text/html139 <p>Basic text.</p>140 """))141 self.assertEqual(raw_data_manager.get_content(m),142 "<p>Basic text.</p>\n")143 def test_get_text_plain_latin1(self):144 m = self._bytes_msg(textwrap.dedent("""\145 Content-Type: text/plain; charset=latin1146 Basìc tëxt.147 """).encode('latin1'))148 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")149 def test_get_text_plain_latin1_quoted_printable(self):150 m = self._str_msg(textwrap.dedent("""\151 Content-Type: text/plain; charset="latin-1"152 Content-Transfer-Encoding: quoted-printable153 Bas=ECc t=EBxt.154 """))155 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")156 def test_get_text_plain_utf8_base64(self):157 m = self._str_msg(textwrap.dedent("""\158 Content-Type: text/plain; charset="utf8"159 Content-Transfer-Encoding: base64160 QmFzw6xjIHTDq3h0Lgo=161 """))162 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")163 def test_get_text_plain_bad_utf8_quoted_printable(self):164 m = self._str_msg(textwrap.dedent("""\165 Content-Type: text/plain; charset="utf8"166 Content-Transfer-Encoding: quoted-printable167 Bas=c3=acc t=c3=abxt=fd.168 """))169 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt�.\n")170 def test_get_text_plain_bad_utf8_quoted_printable_ignore_errors(self):171 m = self._str_msg(textwrap.dedent("""\172 Content-Type: text/plain; charset="utf8"173 Content-Transfer-Encoding: quoted-printable174 Bas=c3=acc t=c3=abxt=fd.175 """))176 self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),177 "Basìc tëxt.\n")178 def test_get_text_plain_utf8_base64_recoverable_bad_CTE_data(self):179 m = self._str_msg(textwrap.dedent("""\180 Content-Type: text/plain; charset="utf8"181 Content-Transfer-Encoding: base64182 QmFzw6xjIHTDq3h0Lgo\xFF=183 """))184 self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),185 "Basìc tëxt.\n")186 def test_get_text_invalid_keyword(self):187 m = self._str_msg(textwrap.dedent("""\188 Content-Type: text/plain189 Basic text.190 """))191 with self.assertRaises(TypeError):192 raw_data_manager.get_content(m, foo='ignore')193 def test_get_non_text(self):194 template = textwrap.dedent("""\195 Content-Type: {}196 Content-Transfer-Encoding: base64197 Ym9ndXMgZGF0YQ==198 """)199 for maintype in 'audio image video application'.split():200 with self.subTest(maintype=maintype):201 m = self._str_msg(template.format(maintype+'/foo'))202 self.assertEqual(raw_data_manager.get_content(m), b"bogus data")203 def test_get_non_text_invalid_keyword(self):204 m = self._str_msg(textwrap.dedent("""\205 Content-Type: image/jpg206 Content-Transfer-Encoding: base64207 Ym9ndXMgZGF0YQ==208 """))209 with self.assertRaises(TypeError):210 raw_data_manager.get_content(m, errors='ignore')211 def test_get_raises_on_multipart(self):212 m = self._str_msg(textwrap.dedent("""\213 Content-Type: multipart/mixed; boundary="==="214 --===215 --===--216 """))217 with self.assertRaises(KeyError):218 raw_data_manager.get_content(m)219 def test_get_message_rfc822_and_external_body(self):220 template = textwrap.dedent("""\221 Content-Type: message/{}222 To: foo@example.com223 From: bar@example.com224 Subject: example225 an example message226 """)227 for subtype in 'rfc822 external-body'.split():228 with self.subTest(subtype=subtype):229 m = self._str_msg(template.format(subtype))230 sub_msg = raw_data_manager.get_content(m)231 self.assertIsInstance(sub_msg, self.message)232 self.assertEqual(raw_data_manager.get_content(sub_msg),233 "an example message\n")234 self.assertEqual(sub_msg['to'], 'foo@example.com')235 self.assertEqual(sub_msg['from'].addresses[0].username, 'bar')236 def test_get_message_non_rfc822_or_external_body_yields_bytes(self):237 m = self._str_msg(textwrap.dedent("""\238 Content-Type: message/partial239 To: foo@example.com240 From: bar@example.com241 Subject: example242 The real body is in another message.243 """))244 self.assertEqual(raw_data_manager.get_content(m)[:10], b'To: foo@ex')245 def test_set_text_plain(self):246 m = self._make_message()247 content = "Simple message.\n"248 raw_data_manager.set_content(m, content)249 self.assertEqual(str(m), textwrap.dedent("""\250 Content-Type: text/plain; charset="utf-8"251 Content-Transfer-Encoding: 7bit252 Simple message.253 """))254 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)255 self.assertEqual(m.get_content(), content)256 def test_set_text_html(self):257 m = self._make_message()258 content = "<p>Simple message.</p>\n"259 raw_data_manager.set_content(m, content, subtype='html')260 self.assertEqual(str(m), textwrap.dedent("""\261 Content-Type: text/html; charset="utf-8"262 Content-Transfer-Encoding: 7bit263 <p>Simple message.</p>264 """))265 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)266 self.assertEqual(m.get_content(), content)267 def test_set_text_charset_latin_1(self):268 m = self._make_message()269 content = "Simple message.\n"270 raw_data_manager.set_content(m, content, charset='latin-1')271 self.assertEqual(str(m), textwrap.dedent("""\272 Content-Type: text/plain; charset="iso-8859-1"273 Content-Transfer-Encoding: 7bit274 Simple message.275 """))276 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)277 self.assertEqual(m.get_content(), content)278 def test_set_text_short_line_minimal_non_ascii_heuristics(self):279 m = self._make_message()280 content = "et là il est monté sur moi et il commence à m'éto.\n"281 raw_data_manager.set_content(m, content)282 self.assertEqual(bytes(m), textwrap.dedent("""\283 Content-Type: text/plain; charset="utf-8"284 Content-Transfer-Encoding: 8bit285 et là il est monté sur moi et il commence à m'éto.286 """).encode('utf-8'))287 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)288 self.assertEqual(m.get_content(), content)289 def test_set_text_long_line_minimal_non_ascii_heuristics(self):290 m = self._make_message()291 content = ("j'ai un problème de python. il est sorti de son"292 " vivarium. et là il est monté sur moi et il commence"293 " à m'éto.\n")294 raw_data_manager.set_content(m, content)295 self.assertEqual(bytes(m), textwrap.dedent("""\296 Content-Type: text/plain; charset="utf-8"297 Content-Transfer-Encoding: quoted-printable298 j'ai un probl=C3=A8me de python. il est sorti de son vivari=299 um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =300 =C3=A0 m'=C3=A9to.301 """).encode('utf-8'))302 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)303 self.assertEqual(m.get_content(), content)304 def test_set_text_11_lines_long_line_minimal_non_ascii_heuristics(self):305 m = self._make_message()306 content = '\n'*10 + (307 "j'ai un problème de python. il est sorti de son"308 " vivarium. et là il est monté sur moi et il commence"309 " à m'éto.\n")310 raw_data_manager.set_content(m, content)311 self.assertEqual(bytes(m), textwrap.dedent("""\312 Content-Type: text/plain; charset="utf-8"313 Content-Transfer-Encoding: quoted-printable314 """ + '\n'*10 + """315 j'ai un probl=C3=A8me de python. il est sorti de son vivari=316 um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =317 =C3=A0 m'=C3=A9to.318 """).encode('utf-8'))319 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)320 self.assertEqual(m.get_content(), content)321 def test_set_text_maximal_non_ascii_heuristics(self):322 m = self._make_message()323 content = "áà äéèÄöÅ.\n"324 raw_data_manager.set_content(m, content)325 self.assertEqual(bytes(m), textwrap.dedent("""\326 Content-Type: text/plain; charset="utf-8"327 Content-Transfer-Encoding: 8bit328 áà äéèÄöÅ.329 """).encode('utf-8'))330 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)331 self.assertEqual(m.get_content(), content)332 def test_set_text_11_lines_maximal_non_ascii_heuristics(self):333 m = self._make_message()334 content = '\n'*10 + "áà äéèÄöÅ.\n"335 raw_data_manager.set_content(m, content)336 self.assertEqual(bytes(m), textwrap.dedent("""\337 Content-Type: text/plain; charset="utf-8"338 Content-Transfer-Encoding: 8bit339 """ + '\n'*10 + """340 áà äéèÄöÅ.341 """).encode('utf-8'))342 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)343 self.assertEqual(m.get_content(), content)344 def test_set_text_long_line_maximal_non_ascii_heuristics(self):345 m = self._make_message()346 content = ("áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"347 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"348 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")349 raw_data_manager.set_content(m, content)350 self.assertEqual(bytes(m), textwrap.dedent("""\351 Content-Type: text/plain; charset="utf-8"352 Content-Transfer-Encoding: base64353 w6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOoxJnD354 tsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOo355 xJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TD356 qcOoxJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOg357 w6TDqcOoxJnDtsWRLgo=358 """).encode('utf-8'))359 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)360 self.assertEqual(m.get_content(), content)361 def test_set_text_11_lines_long_line_maximal_non_ascii_heuristics(self):362 # Yes, it chooses "wrong" here. It's a heuristic. So this result363 # could change if we come up with a better heuristic.364 m = self._make_message()365 content = ('\n'*10 +366 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"367 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"368 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")369 raw_data_manager.set_content(m, "\n"*10 +370 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"371 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"372 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")373 self.assertEqual(bytes(m), textwrap.dedent("""\374 Content-Type: text/plain; charset="utf-8"375 Content-Transfer-Encoding: quoted-printable376 """ + '\n'*10 + """377 =C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=378 =A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=379 =C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=380 =A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=381 =C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=382 =91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=383 =C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=384 =A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=385 =C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=386 =99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=387 =C5=91.388 """).encode('utf-8'))389 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)390 self.assertEqual(m.get_content(), content)391 def test_set_text_non_ascii_with_cte_7bit_raises(self):392 m = self._make_message()393 with self.assertRaises(UnicodeError):394 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", cte='7bit')395 def test_set_text_non_ascii_with_charset_ascii_raises(self):396 m = self._make_message()397 with self.assertRaises(UnicodeError):398 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", charset='ascii')399 def test_set_text_non_ascii_with_cte_7bit_and_charset_ascii_raises(self):400 m = self._make_message()401 with self.assertRaises(UnicodeError):402 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", cte='7bit', charset='ascii')403 def test_set_message(self):404 m = self._make_message()405 m['Subject'] = "Forwarded message"406 content = self._make_message()407 content['To'] = 'python@vivarium.org'408 content['From'] = 'police@monty.org'409 content['Subject'] = "get back in your box"410 content.set_content("Or face the comfy chair.")411 raw_data_manager.set_content(m, content)412 self.assertEqual(str(m), textwrap.dedent("""\413 Subject: Forwarded message414 Content-Type: message/rfc822415 Content-Transfer-Encoding: 8bit416 To: python@vivarium.org417 From: police@monty.org418 Subject: get back in your box419 Content-Type: text/plain; charset="utf-8"420 Content-Transfer-Encoding: 7bit421 MIME-Version: 1.0422 Or face the comfy chair.423 """))424 payload = m.get_payload(0)425 self.assertIsInstance(payload, self.message)426 self.assertEqual(str(payload), str(content))427 self.assertIsInstance(m.get_content(), self.message)428 self.assertEqual(str(m.get_content()), str(content))429 def test_set_message_with_non_ascii_and_coercion_to_7bit(self):430 m = self._make_message()431 m['Subject'] = "Escape report"432 content = self._make_message()433 content['To'] = 'police@monty.org'434 content['From'] = 'victim@monty.org'435 content['Subject'] = "Help"436 content.set_content("j'ai un problème de python. il est sorti de son"437 " vivarium.")438 raw_data_manager.set_content(m, content)439 self.assertEqual(bytes(m), textwrap.dedent("""\440 Subject: Escape report441 Content-Type: message/rfc822442 Content-Transfer-Encoding: 8bit443 To: police@monty.org444 From: victim@monty.org445 Subject: Help446 Content-Type: text/plain; charset="utf-8"447 Content-Transfer-Encoding: 8bit448 MIME-Version: 1.0449 j'ai un problème de python. il est sorti de son vivarium.450 """).encode('utf-8'))451 # The choice of base64 for the body encoding is because generator452 # doesn't bother with heuristics and uses it unconditionally for utf-8453 # text.454 # XXX: the first cte should be 7bit, too...that's a generator bug.455 # XXX: the line length in the body also looks like a generator bug.456 self.assertEqual(m.as_string(maxheaderlen=self.policy.max_line_length),457 textwrap.dedent("""\458 Subject: Escape report459 Content-Type: message/rfc822460 Content-Transfer-Encoding: 8bit461 To: police@monty.org462 From: victim@monty.org463 Subject: Help464 Content-Type: text/plain; charset="utf-8"465 Content-Transfer-Encoding: base64466 MIME-Version: 1.0467 aidhaSB1biBwcm9ibMOobWUgZGUgcHl0aG9uLiBpbCBlc3Qgc29ydGkgZGUgc29uIHZpdmFyaXVt468 Lgo=469 """))470 self.assertIsInstance(m.get_content(), self.message)471 self.assertEqual(str(m.get_content()), str(content))472 def test_set_message_invalid_cte_raises(self):473 m = self._make_message()474 content = self._make_message()475 for cte in 'quoted-printable base64'.split():476 for subtype in 'rfc822 external-body'.split():477 with self.subTest(cte=cte, subtype=subtype):478 with self.assertRaises(ValueError) as ar:479 m.set_content(content, subtype, cte=cte)480 exc = str(ar.exception)481 self.assertIn(cte, exc)482 self.assertIn(subtype, exc)483 subtype = 'external-body'484 for cte in '8bit binary'.split():485 with self.subTest(cte=cte, subtype=subtype):486 with self.assertRaises(ValueError) as ar:487 m.set_content(content, subtype, cte=cte)488 exc = str(ar.exception)489 self.assertIn(cte, exc)490 self.assertIn(subtype, exc)491 def test_set_image_jpg(self):492 for content in (b"bogus content",493 bytearray(b"bogus content"),494 memoryview(b"bogus content")):495 with self.subTest(content=content):496 m = self._make_message()497 raw_data_manager.set_content(m, content, 'image', 'jpeg')498 self.assertEqual(str(m), textwrap.dedent("""\499 Content-Type: image/jpeg500 Content-Transfer-Encoding: base64501 Ym9ndXMgY29udGVudA==502 """))503 self.assertEqual(m.get_payload(decode=True), content)504 self.assertEqual(m.get_content(), content)505 def test_set_audio_aif_with_quoted_printable_cte(self):506 # Why you would use qp, I don't know, but it is technically supported.507 # XXX: the incorrect line length is because binascii.b2a_qp doesn't508 # support a line length parameter, but we must use it to get newline509 # encoding.510 # XXX: what about that lack of tailing newline? Do we actually handle511 # that correctly in all cases? That is, if the *source* has an512 # unencoded newline, do we add an extra newline to the returned payload513 # or not? And can that actually be disambiguated based on the RFC?514 m = self._make_message()515 content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100516 m.set_content(content, 'audio', 'aif', cte='quoted-printable')517 self.assertEqual(bytes(m), textwrap.dedent("""\518 Content-Type: audio/aif519 Content-Transfer-Encoding: quoted-printable520 MIME-Version: 1.0521 b=FFgus=09con=0At=0Dent=20zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=522 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""").encode('latin-1'))523 self.assertEqual(m.get_payload(decode=True), content)524 self.assertEqual(m.get_content(), content)525 def test_set_video_mpeg_with_binary_cte(self):526 m = self._make_message()527 content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100528 m.set_content(content, 'video', 'mpeg', cte='binary')529 self.assertEqual(bytes(m), textwrap.dedent("""\530 Content-Type: video/mpeg531 Content-Transfer-Encoding: binary532 MIME-Version: 1.0533 """).encode('ascii') +534 # XXX: the second \n ought to be a \r, but generator gets it wrong.535 # THIS MEANS WE DON'T ACTUALLY SUPPORT THE 'binary' CTE.536 b'b\xFFgus\tcon\nt\nent zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' +537 b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')538 self.assertEqual(m.get_payload(decode=True), content)539 self.assertEqual(m.get_content(), content)540 def test_set_application_octet_stream_with_8bit_cte(self):541 # In 8bit mode, univeral line end logic applies. It is up to the542 # application to make sure the lines are short enough; we don't check.543 m = self._make_message()544 content = b'b\xFFgus\tcon\nt\rent\n' + b'z'*60 + b'\n'545 m.set_content(content, 'application', 'octet-stream', cte='8bit')546 self.assertEqual(bytes(m), textwrap.dedent("""\547 Content-Type: application/octet-stream548 Content-Transfer-Encoding: 8bit549 MIME-Version: 1.0550 """).encode('ascii') +551 b'b\xFFgus\tcon\nt\nent\n' +552 b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n')553 self.assertEqual(m.get_payload(decode=True), content)554 self.assertEqual(m.get_content(), content)555 def test_set_headers_from_header_objects(self):556 m = self._make_message()557 content = "Simple message.\n"558 header_factory = self.policy.header_factory559 raw_data_manager.set_content(m, content, headers=(560 header_factory("To", "foo@example.com"),561 header_factory("From", "foo@example.com"),562 header_factory("Subject", "I'm talking to myself.")))563 self.assertEqual(str(m), textwrap.dedent("""\564 Content-Type: text/plain; charset="utf-8"565 To: foo@example.com566 From: foo@example.com567 Subject: I'm talking to myself.568 Content-Transfer-Encoding: 7bit569 Simple message.570 """))571 def test_set_headers_from_strings(self):572 m = self._make_message()573 content = "Simple message.\n"574 raw_data_manager.set_content(m, content, headers=(575 "X-Foo-Header: foo",576 "X-Bar-Header: bar",))577 self.assertEqual(str(m), textwrap.dedent("""\578 Content-Type: text/plain; charset="utf-8"579 X-Foo-Header: foo580 X-Bar-Header: bar581 Content-Transfer-Encoding: 7bit582 Simple message.583 """))584 def test_set_headers_with_invalid_duplicate_string_header_raises(self):585 m = self._make_message()586 content = "Simple message.\n"587 with self.assertRaisesRegex(ValueError, 'Content-Type'):588 raw_data_manager.set_content(m, content, headers=(589 "Content-Type: foo/bar",)590 )591 def test_set_headers_with_invalid_duplicate_header_header_raises(self):592 m = self._make_message()593 content = "Simple message.\n"594 header_factory = self.policy.header_factory595 with self.assertRaisesRegex(ValueError, 'Content-Type'):596 raw_data_manager.set_content(m, content, headers=(597 header_factory("Content-Type", " foo/bar"),)598 )599 def test_set_headers_with_defective_string_header_raises(self):600 m = self._make_message()601 content = "Simple message.\n"602 with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):603 raw_data_manager.set_content(m, content, headers=(604 'To: a@fairly@@invalid@address',)605 )606 print(m['To'].defects)607 def test_set_headers_with_defective_header_header_raises(self):608 m = self._make_message()609 content = "Simple message.\n"610 header_factory = self.policy.header_factory611 with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):612 raw_data_manager.set_content(m, content, headers=(613 header_factory('To', 'a@fairly@@invalid@address'),)614 )615 print(m['To'].defects)616 def test_set_disposition_inline(self):617 m = self._make_message()618 m.set_content('foo', disposition='inline')619 self.assertEqual(m['Content-Disposition'], 'inline')620 def test_set_disposition_attachment(self):621 m = self._make_message()622 m.set_content('foo', disposition='attachment')623 self.assertEqual(m['Content-Disposition'], 'attachment')624 def test_set_disposition_foo(self):625 m = self._make_message()626 m.set_content('foo', disposition='foo')627 self.assertEqual(m['Content-Disposition'], 'foo')628 # XXX: we should have a 'strict' policy mode (beyond raise_on_defect) that629 # would cause 'foo' above to raise.630 def test_set_filename(self):631 m = self._make_message()632 m.set_content('foo', filename='bar.txt')633 self.assertEqual(m['Content-Disposition'],634 'attachment; filename="bar.txt"')635 def test_set_filename_and_disposition_inline(self):636 m = self._make_message()637 m.set_content('foo', disposition='inline', filename='bar.txt')638 self.assertEqual(m['Content-Disposition'], 'inline; filename="bar.txt"')639 def test_set_non_ascii_filename(self):640 m = self._make_message()641 m.set_content('foo', filename='ábárî.txt')642 self.assertEqual(bytes(m), textwrap.dedent("""\643 Content-Type: text/plain; charset="utf-8"644 Content-Transfer-Encoding: 7bit645 Content-Disposition: attachment;646 filename*=utf-8''%C3%A1b%C3%A1r%C3%AE.txt647 MIME-Version: 1.0648 foo649 """).encode('ascii'))650 content_object_params = {651 'text_plain': ('content', ()),652 'text_html': ('content', ('html',)),653 'application_octet_stream': (b'content',654 ('application', 'octet_stream')),655 'image_jpeg': (b'content', ('image', 'jpeg')),656 'message_rfc822': (message(), ()),657 'message_external_body': (message(), ('external-body',)),658 }659 def content_object_as_header_receiver(self, obj, mimetype):660 m = self._make_message()661 m.set_content(obj, *mimetype, headers=(662 'To: foo@example.com',663 'From: bar@simple.net'))664 self.assertEqual(m['to'], 'foo@example.com')665 self.assertEqual(m['from'], 'bar@simple.net')666 def content_object_as_disposition_inline_receiver(self, obj, mimetype):667 m = self._make_message()668 m.set_content(obj, *mimetype, disposition='inline')669 self.assertEqual(m['Content-Disposition'], 'inline')670 def content_object_as_non_ascii_filename_receiver(self, obj, mimetype):671 m = self._make_message()672 m.set_content(obj, *mimetype, disposition='inline', filename='bár.txt')673 self.assertEqual(m['Content-Disposition'], 'inline; filename="bár.txt"')674 self.assertEqual(m.get_filename(), "bár.txt")675 self.assertEqual(m['Content-Disposition'].params['filename'], "bár.txt")676 def content_object_as_cid_receiver(self, obj, mimetype):677 m = self._make_message()678 m.set_content(obj, *mimetype, cid='some_random_stuff')679 self.assertEqual(m['Content-ID'], 'some_random_stuff')680 def content_object_as_params_receiver(self, obj, mimetype):681 m = self._make_message()682 params = {'foo': 'bár', 'abc': 'xyz'}683 m.set_content(obj, *mimetype, params=params)684 if isinstance(obj, str):685 params['charset'] = 'utf-8'686 self.assertEqual(m['Content-Type'].params, params)687if __name__ == '__main__':...
default.py
Source:default.py
1#!/usr/bin/python2# -*- coding: utf-8 -*-3import xbmc, xbmcgui, xbmcaddon, urllib, urllib2, socket, cookielib, re, os, shutil, base64, xbmcvfs4addon = xbmcaddon.Addon()5socket.setdefaulttimeout(60)6addonID = addon.getAddonInfo('id')7translation = addon.getLocalizedString8addonUserDataFolder=xbmc.translatePath("special://profile/addon_data/"+addonID)9subTempDir=xbmc.translatePath("special://profile/addon_data/"+addonID+"/srtTemp/")10icon = xbmc.translatePath('special://home/addons/'+addonID+'/icon.png')11rarFile=xbmc.translatePath(addonUserDataFolder+"/sub.")12subFile=xbmc.translatePath(addonUserDataFolder+"/sub.srt")13favFile=xbmc.translatePath(addonUserDataFolder+"/favourites")14apiKeyFile=xbmc.translatePath(addonUserDataFolder+"/api.key")15if not os.path.isdir(addonUserDataFolder):16 os.mkdir(addonUserDataFolder)17if not os.path.isdir(subTempDir):18 os.mkdir(subTempDir)19if os.path.exists(apiKeyFile):20 fh = open(apiKeyFile, 'r')21 ownKey = fh.read()22 fh.close()23else:24 ownKey=""25user=""26pw=""27backNav=""28pause=""29saveSub=""30language=""31def getSettings():32 global user33 user=addon.getSetting("user")34 global pw35 pw=addon.getSetting("pw")36 global backNav37 backNav=addon.getSetting("backNav")38 global pause39 pause=addon.getSetting("pause")40 global saveSub41 saveSub=addon.getSetting("saveSub")42 global language43 language=addon.getSetting("language")44getSettings()45if pause=="true" and xbmc.Player().isPlayingVideo():46 xbmc.Player().pause()47playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)48if playlist.getposition()>=0:49 currentTitle = playlist[playlist.getposition()].getdescription()50 51currentFile = xbmc.Player().getPlayingFile()52cj = cookielib.CookieJar()53mainUrl = "http://www.subcentral.de"54opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))55userAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0"56opener.addheaders = [('User-Agent', userAgent)]57opener.open(mainUrl+"/index.php?form=UserLogin", data="loginUsername="+urllib.quote_plus(user)+"&loginPassword="+urllib.quote_plus(pw))58while (user=="" or pw==""):59 addon.openSettings()60 getSettings()61currentEpisode=""62currentSeason=""63currentEpisode=xbmc.getInfoLabel('VideoPlayer.Episode')64currentSeason=xbmc.getInfoLabel('VideoPlayer.Season')65dirName = ""66try:67 dirName=(currentFile.split(os.sep)[-2]).lower()68except:69 pass70fileName=os.path.basename(currentFile).lower()71if currentEpisode=="":72 matchDir=re.compile('\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(dirName)73 matchFile=re.compile('\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(fileName)74 if len(matchDir)>0:75 currentSeason=matchDir[0][0]76 currentEpisode=matchDir[0][1]77 elif len(matchFile)>0:78 currentSeason=matchFile[0][0]79 currentEpisode=matchFile[0][1]80if currentEpisode=="":81 match=re.compile('(.+?)- s(.+?)e(.+?) ', re.DOTALL).findall(xbmc.getInfoLabel('VideoPlayer.Title').lower())82 if len(match)>0:83 currentSeason=match[0][1]84 currentEpisode=match[0][2]85if len(currentEpisode)==1:86 currentEpisode="0"+currentEpisode87if len(currentSeason)==1:88 currentSeason="0"+currentSeason89currentRelease=""90if "-" in fileName:91 currentRelease=(fileName.split("-")[-1]).lower()92 if "." in currentRelease:93 currentRelease=currentRelease[:currentRelease.find(".")]94elif "-" in dirName:95 currentRelease=(dirName.split("-")[-1]).lower()96def main():97 global mainType98 mainType = ""99 dialog = xbmcgui.Dialog()100 nr=dialog.select("SubCentral.de", [translation(30013),translation(30001),translation(30002),translation(30011)])101 if nr==0:102 search()103 elif nr==1:104 mainType="fav"105 showFavourites()106 elif nr==2:107 mainType="all"108 showAllSeries()109 elif nr==3:110 addon.openSettings()111 getSettings()112 main()113 else:114 if pause=="true" and xbmc.Player().isPlayingVideo():115 xbmc.Player().pause()116def search():117 title=xbmc.getInfoLabel('VideoPlayer.TVShowTitle')118 season=currentSeason119 episode=currentEpisode120 release=currentRelease121 122 if title=="" or season=="":123 matchDir=re.compile('(.+?)\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(dirName)124 matchFile=re.compile('(.+?)\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(fileName)125 matchTitle=re.compile('(.+?)- s(.+?)e(.+?) ', re.DOTALL).findall(xbmc.getInfoLabel('VideoPlayer.Title').lower())126 if len(matchDir)>0:127 title=matchDir[0][0]128 elif len(matchFile)>0:129 title=matchFile[0][0]130 elif len(matchTitle)>0:131 title=matchTitle[0][0].strip()132 133 title=title.replace("."," ")134 if "(" in title:135 title=title[:title.find("(")].strip()136 137 if title=="" or season=="" or episode=="":138 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30014)+'!,3000,'+icon+')')139 main()140 else:141 if season[0:1]=="0":142 season=season[1:]143 general=base64.b64decode("QUl6YVN5RGJtNzRTNlZia1VjWmNQeC1HTTFtU1B5N2ZYU0R2Vy1J")144 global ownKey145 if ownKey=="":146 ownKey=general147 searchString='intitle:"[subs] '+title+' - staffel '+season+'|0'+season+'"'148 fullUrl="https://www.googleapis.com/customsearch/v1?key="+ownKey+"&cx=016144106520845837387:lj4yjfpwyna&q="+urllib.quote_plus(searchString)+"&alt=json"149 xbmc.log("SCDE Addon Log - Search: "+title+"#"+season)150 content = getUrl(fullUrl)151 if '"items"' in content:152 content = content[content.find('"items"'):]153 spl=content.split('"kind"')154 finalUrl=""155 for i in range(1,len(spl),1):156 entry=spl[i]157 match=re.compile('"title": "(.+?)"', re.DOTALL).findall(entry)158 title=match[0]159 match=re.compile('"link": "(.+?)"', re.DOTALL).findall(entry)160 url=match[0]161 if "staffel" in title.lower() and "subs:" in title.lower() and "postID=" not in url and "boardID=" not in url:162 finalUrl=url163 break164 if finalUrl=="":165 for i in range(1,len(spl),1):166 entry=spl[i]167 match=re.compile('"title": "(.+?)"', re.DOTALL).findall(entry)168 title=match[0]169 match=re.compile('"link": "(.+?)"', re.DOTALL).findall(entry)170 url=match[0]171 if "staffel" in title.lower() and "postID=" not in url and "boardID=" not in url:172 finalUrl=url173 break174 175 if len(season)==1:176 season="0"+season177 178 if finalUrl!="":179 threadID = finalUrl[finalUrl.find("&threadID=")+10:]180 if "&" in threadID:181 threadID = threadID[:threadID.find("&")]182 content = opener.open("http://www.subcentral.de/index.php?page=Thread&threadID="+threadID).read()183 if 'class="bedankhinweisSC' in content:184 contentThanks=content185 contentThanks=contentThanks[contentThanks.find('class="bedankhinweisSC'):]186 match=re.compile('href="index.php\\?action=Thank(.+?)"', re.DOTALL).findall(contentThanks)187 matchID=re.compile('name="threadID" value="(.+?)"', re.DOTALL).findall(contentThanks)188 dialog = xbmcgui.Dialog()189 nr=dialog.select(title, [translation(30005)+"..."])190 if nr>=0:191 opener.open(mainUrl+"/index.php?action=Thank"+match[0].replace("&","&"))192 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+matchID[0]).read()193 194 attachments = []195 titles = []196 197 match=re.compile('<title>(.+?)-', re.DOTALL).findall(content)198 tvShowTitle=match[0].replace("[Subs]","").strip()199 200 content = content[content.find("quoteData.set('post-")+1:]201 content = content[:content.find("quoteData.set('post-")]202 contentDE=content203 contentEN=""204 if '<img src="creative/bilder/flags/usa.png"' in content:205 contentDE=content[:content.find('<img src="creative/bilder/flags/usa.png"')]206 contentEN=content[content.find('<img src="creative/bilder/flags/usa.png"'):]207 elif '<img src="creative/bilder/flags/uk.png"' in content:208 contentDE=content[:content.find('<img src="creative/bilder/flags/uk.png"')]209 contentEN=content[content.find('<img src="creative/bilder/flags/uk.png"'):]210 elif 'Englische Untertitel:' in content:211 contentDE=content[:content.find('Englische Untertitel:')]212 contentEN=content[content.find('Englische Untertitel:'):]213 if "page=Attachment&attachmentID=" not in contentDE:214 contentDE=content215 216 if language=="0":217 tempDE=appendSubInfo(tvShowTitle,season,episode,release,contentDE,"DE")218 attachments += tempDE[0]219 titles += tempDE[1]220 if contentEN!="":221 tempEN=appendSubInfo(tvShowTitle,season,episode,release,contentEN,"EN")222 attachments += tempEN[0]223 titles += tempEN[1]224 elif language=="1":225 tempDE=appendSubInfo(tvShowTitle,season,episode,release,contentDE,"DE")226 attachments += tempDE[0]227 titles += tempDE[1]228 elif language=="2" and contentEN!="":229 tempEN=appendSubInfo(tvShowTitle,season,episode,release,contentEN,"EN")230 attachments += tempEN[0]231 titles += tempEN[1]232 233 if len(titles)>0:234 titles, attachments = (list(x) for x in zip(*sorted(zip(titles, attachments))))235 dialog = xbmcgui.Dialog()236 nr=dialog.select(os.path.basename(currentFile), titles)237 if nr>=0:238 subUrl=mainUrl+"/index.php?page=Attachment&attachmentID="+attachments[nr]239 setSubtitle(subUrl)240 elif backNav=="true":241 main()242 else:243 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30015)+'!,3000,'+icon+')')244 main()245 elif '"totalResults": "0"' in content:246 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30015)+'!,3000,'+icon+')')247 main()248 else:249 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30016)+'!,10000,'+icon+')')250 main()251def getEpisodes(entry):252 ep=ep2=""253 match=re.compile('>E(.+?) -', re.DOTALL).findall(entry,0,10)254 match2=re.compile('>(.+?)x(.+?) -', re.DOTALL).findall(entry,0,10)255 match3=re.compile('>(.+?)\\. ', re.DOTALL).findall(entry,0,10)256 match4=re.compile('>(.+?) -', re.DOTALL).findall(entry,0,10)257 match5=re.compile('>(.+?)<', re.DOTALL).findall(entry,0,10)258 if "- komplett<" in entry.lower():259 ep=ep2=" - Komplett"260 else:261 if len(match)>0:262 ep=match[0]263 elif len(match2):264 ep=match2[0][1]265 elif len(match3):266 ep=match3[0]267 elif len(match4):268 ep=match4[0]269 elif len(match5):270 ep=match5[0]271 else:272 ep="00"273 ep2="E"+ep274 return [ep,ep2]275def appendSubInfo(tvShowTitle,season,episode,release,content,lang):276 attachments1 = []277 titles1 = []278 content2=content.replace('<span class=" Stil36 Stil31">','').replace('<span class="Stil37">','')279 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)280 for attach, ep, rel in match:281 ep2="E"+ep282 if episode==ep:283 check = release==""284 if release!="":285 check = release==rel.lower()286 if check:287 if attach not in attachments1:288 attachments1.append(attach)289 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))290 if len(attachments1)==0:291 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)292 for attach, ep, rel in match:293 ep2="E"+ep294 if episode==ep:295 if attach not in attachments1:296 attachments1.append(attach)297 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))298 if len(attachments1)==0:299 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)300 for attach, ep, rel in match:301 ep2="E"+ep302 if attach not in attachments1:303 attachments1.append(attach)304 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))305 attachments2 = []306 titles2 = []307 splitStr = ""308 if 'class="release"' in content:309 splitStr = 'class="release"'310 elif 'class="Stil9"' in content:311 splitStr = 'class="Stil9"'312 if splitStr:313 spl=content.split(splitStr)314 for i in range(1,len(spl),1):315 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')316 temp = getEpisodes(entry)317 ep = temp[0]318 ep2 = temp[1]319 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)320 for attach, rel in match:321 if episode==ep:322 check = release==""323 if release!="":324 check = release==rel.lower()325 if check:326 if attach not in attachments2:327 attachments2.append(attach)328 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))329 if len(attachments2)==0:330 spl=content.split(splitStr)331 for i in range(1,len(spl),1):332 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')333 temp = getEpisodes(entry)334 ep = temp[0]335 ep2 = temp[1]336 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)337 for attach, rel in match:338 if episode==ep:339 if attach not in attachments2:340 attachments2.append(attach)341 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))342 if len(attachments2)==0:343 spl=content.split(splitStr)344 for i in range(1,len(spl),1):345 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')346 temp = getEpisodes(entry)347 ep = temp[0]348 ep2 = temp[1]349 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)350 for attach, rel in match:351 if attach not in attachments2:352 attachments2.append(attach)353 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))354 return [attachments1+attachments2,titles1+titles2]355def showFavourites():356 ids = []357 titles = []358 if os.path.exists(favFile):359 fh = open(favFile, 'r')360 for line in fh:361 id = line[:line.find("#")]362 title = line[line.find("#")+1:]363 title = title[:title.find("#END")]364 ids.append(id)365 titles.append(title)366 fh.close()367 titles, ids = (list(x) for x in zip(*sorted(zip(titles, ids))))368 dialog = xbmcgui.Dialog()369 nr=dialog.select(translation(30001), titles)370 if nr>=0:371 id=ids[nr]372 title=titles[nr]373 showSeries(id)374 elif backNav=="true":375 main()376def showAllSeries():377 content = opener.open(mainUrl+"/index.php").read()378 content = content[content.find('<option value=""> Serien QuickJump </option>')+1:]379 content = content[:content.find('</form>')]380 match=re.compile('<option value="(.+?)">(.+?)</option>', re.DOTALL).findall(content)381 threadIDs = []382 threadNames = []383 for id, title in match:384 threadIDs.append(id)385 threadNames.append(title)386 dialog = xbmcgui.Dialog()387 nr=dialog.select(translation(30002), threadNames)388 if nr>=0:389 id=threadIDs[nr]390 title=threadNames[nr]391 showSeries(id)392 elif backNav=="true":393 main()394def showSeries(seriesID):395 content = opener.open(mainUrl+"/index.php?page=Board&boardID="+seriesID).read()396 match=re.compile('<title>(.+?) -', re.DOTALL).findall(content)397 SeriesTitle=match[0]398 content = content[content.find("<h3>Wichtige Themen</h3>"):]399 content = content[:content.find('</table>')]400 spl=content.split('<p id="threadTitle')401 threadIDs = []402 threadNames = []403 season=currentSeason404 if season[0:1]=="0":405 season=season[1:]406 for i in range(1,len(spl),1):407 entry=spl[i]408 match=re.compile('<a href="index.php\\?page=Thread&threadID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)409 if ("staffel "+season in match[0][1].lower() or "staffel 0"+season in match[0][1].lower()) and "subs" in match[0][1].lower():410 threadIDs.append(match[0][0])411 threadNames.append(cleanTitle(match[0][1]))412 if len(threadIDs)==0:413 for i in range(1,len(spl),1):414 entry=spl[i]415 match=re.compile('<a href="index.php\\?page=Thread&threadID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)416 if "subs" in match[0][1].lower():417 threadIDs.append(match[0][0])418 threadNames.append(cleanTitle(match[0][1]))419 threadNames, threadIDs = (list(x) for x in zip(*sorted(zip(threadNames, threadIDs))))420 content=""421 if os.path.exists(favFile):422 fh = open(favFile, 'r')423 content=fh.read()424 fh.close()425 if seriesID+"#" not in content:426 threadNames.append(translation(30003))427 else:428 threadNames.append(translation(30004))429 dialog = xbmcgui.Dialog()430 nr=dialog.select(os.path.basename(currentFile), threadNames)431 if nr>=0:432 if nr==len(threadNames)-1:433 if threadNames[nr]==translation(30003):434 addToFavourites(seriesID,SeriesTitle)435 elif threadNames[nr]==translation(30004):436 removeFromFavourites(seriesID,SeriesTitle)437 showSeries(seriesID)438 else:439 id=threadIDs[nr]440 showSubtitles(seriesID,id)441 elif backNav=="true":442 if mainType=="all":443 showAllSeries()444 elif mainType=="fav":445 showFavourites()446def showSubtitles(seriesID,id):447 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+id).read()448 match=re.compile('<title>(.+?)</title>', re.DOTALL).findall(content)449 title=match[0]450 if 'class="bedankhinweisSC' in content:451 contentThanks=content452 contentThanks=contentThanks[contentThanks.find('class="bedankhinweisSC'):]453 match=re.compile('href="index.php\\?action=Thank(.+?)"', re.DOTALL).findall(contentThanks)454 matchID=re.compile('name="threadID" value="(.+?)"', re.DOTALL).findall(contentThanks)455 dialog = xbmcgui.Dialog()456 nr=dialog.select(title, [translation(30005)+"..."])457 if nr>=0:458 opener.open(mainUrl+"/index.php?action=Thank"+match[0].replace("&","&"))459 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+id).read()460 elif backNav=="true":461 showSeries(seriesID)462 attachments = []463 titles = []464 465 match=re.compile('<title>(.+?)-', re.DOTALL).findall(content)466 tvShowTitle=match[0].replace("[Subs]","").strip()467 match=re.compile('Staffel (.+?) ', re.DOTALL).findall(content)468 season=match[0]469 if len(season)==1:470 season="0"+season471 472 content = content[content.find("quoteData.set('post-")+1:]473 content = content[:content.find("quoteData.set('post-")]474 contentDE=content475 contentEN=""476 if '<img src="creative/bilder/flags/usa.png"' in content:477 contentDE=content[:content.find('<img src="creative/bilder/flags/usa.png"')]478 contentEN=content[content.find('<img src="creative/bilder/flags/usa.png"'):]479 elif '<img src="creative/bilder/flags/uk.png"' in content:480 contentDE=content[:content.find('<img src="creative/bilder/flags/uk.png"')]481 contentEN=content[content.find('<img src="creative/bilder/flags/uk.png"'):]482 elif 'Englische Untertitel:' in content:483 contentDE=content[:content.find('Englische Untertitel:')]484 contentEN=content[content.find('Englische Untertitel:'):]485 if "page=Attachment&attachmentID=" not in contentDE:486 contentDE=content487 488 if language=="0":489 tempDE=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentDE,"DE")490 attachments += tempDE[0]491 titles += tempDE[1]492 if contentEN!="":493 tempEN=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentEN,"EN")494 attachments += tempEN[0]495 titles += tempEN[1]496 elif language=="1":497 tempDE=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentDE,"DE")498 attachments += tempDE[0]499 titles += tempDE[1]500 elif language=="2" and contentEN!="":501 tempEN=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentEN,"EN")502 attachments += tempEN[0]503 titles += tempEN[1]504 505 if len(titles)>0:506 titles, attachments = (list(x) for x in zip(*sorted(zip(titles, attachments))))507 dialog = xbmcgui.Dialog()508 nr=dialog.select(os.path.basename(currentFile), titles)509 if nr>=0:510 subUrl=mainUrl+"/index.php?page=Attachment&attachmentID="+attachments[nr]511 setSubtitle(subUrl)512 elif backNav=="true":513 showSeries(seriesID)514def setSubtitle(subUrl):515 clearSubTempDir()516 rarContent = opener.open(subUrl).read()517 if rarContent.startswith("Rar"):518 ext="rar"519 else:520 ext="zip"521 global rarFile522 rarFile=rarFile+ext523 fh = open(rarFile, 'wb')524 fh.write(rarContent)525 fh.close()526 xbmc.executebuiltin("XBMC.Extract("+rarFile+", "+subTempDir+")")527 xbmc.sleep(1000)528 files = os.listdir(subTempDir)529 tempFile=""530 if len(files)>1:531 dialog = xbmcgui.Dialog()532 nr=dialog.select(currentTitle, files)533 if nr>=0:534 tempFile = xbmc.translatePath(subTempDir+"/"+files[nr])535 else:536 clearSubTempDir()537 if backNav=="true":538 main()539 elif len(files)!=0:540 tempFile = xbmc.translatePath(subTempDir+"/"+files[0])541 else:542 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30017)+'!,3000,'+icon+')')543 if pause=="true" and xbmc.Player().isPlayingVideo():544 xbmc.Player().pause()545 if tempFile!="":546 shutil.copy2(tempFile, subFile)547 if saveSub=="true" and "http://" not in currentFile and "plugin://" not in currentFile:548 try:549 extLength = len(currentFile.split(".")[-1])550 archiveFile = currentFile[:-extLength]+"srt"551 xbmcvfs.copy(tempFile, archiveFile)552 global subFile553 subFile = archiveFile554 except:555 pass556 clearSubTempDir()557 xbmc.Player().setSubtitles(subFile)558 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30012)+'!,2000,'+icon+')')559 if pause=="true" and xbmc.Player().isPlayingVideo():560 xbmc.Player().pause()561def clearSubTempDir():562 files = os.listdir(subTempDir)563 for file in files:564 try:565 os.remove(xbmc.translatePath(subTempDir+"/"+file))566 except:567 pass568def addToFavourites(seriesID,title):569 entry=seriesID+"#"+title+"#END"570 if os.path.exists(favFile):571 fh = open(favFile, 'r')572 content=fh.read()573 fh.close()574 if entry not in content:575 fh=open(favFile, 'a')576 fh.write(entry+"\n")577 fh.close()578 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+title+': '+translation(30008)+',3000,'+icon+')')579 else:580 fh=open(favFile, 'a')581 fh.write(entry+"\n")582 fh.close()583def removeFromFavourites(seriesID,title):584 newContent=""585 fh = open(favFile, 'r')586 for line in fh:587 if seriesID+"#" not in line:588 newContent+=line589 fh.close()590 fh=open(favFile, 'w')591 fh.write(newContent)592 fh.close()593 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+title+': '+translation(30009)+',3000,'+icon+')')594def getUrl(url):595 req = urllib2.Request(url)596 req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:23.0) Gecko/20100101 Firefox/23.0')597 response = urllib2.urlopen(req)598 content=response.read()599 response.close()600 return content601def cleanTitle(title):602 title=title.replace("<","<").replace(">",">").replace("&","&").replace("'","'").replace(""","\"").replace("ß","Ã").replace("–","-")603 title=title.replace("Ä","Ã").replace("Ü","Ã").replace("Ö","Ã").replace("ä","ä").replace("ü","ü").replace("ö","ö")604 title=title.strip()605 return title...
test_message.py
Source:test_message.py
...367 allparts = list(m.walk())368 parts = [allparts[n] for n in parts]369 self.assertEqual(list(m.iter_parts()), parts)370 class _TestContentManager:371 def get_content(self, msg, *args, **kw):372 return msg, args, kw373 def set_content(self, msg, *args, **kw):374 self.msg = msg375 self.args = args376 self.kw = kw377 def test_get_content_with_cm(self):378 m = self._str_msg('')379 cm = self._TestContentManager()380 self.assertEqual(m.get_content(content_manager=cm), (m, (), {}))381 msg, args, kw = m.get_content('foo', content_manager=cm, bar=1, k=2)382 self.assertEqual(msg, m)383 self.assertEqual(args, ('foo',))384 self.assertEqual(kw, dict(bar=1, k=2))385 def test_get_content_default_cm_comes_from_policy(self):386 p = policy.default.clone(content_manager=self._TestContentManager())387 m = self._str_msg('', policy=p)388 self.assertEqual(m.get_content(), (m, (), {}))389 msg, args, kw = m.get_content('foo', bar=1, k=2)390 self.assertEqual(msg, m)391 self.assertEqual(args, ('foo',))392 self.assertEqual(kw, dict(bar=1, k=2))393 def test_set_content_with_cm(self):394 m = self._str_msg('')395 cm = self._TestContentManager()396 m.set_content(content_manager=cm)397 self.assertEqual(cm.msg, m)398 self.assertEqual(cm.args, ())399 self.assertEqual(cm.kw, {})400 m.set_content('foo', content_manager=cm, bar=1, k=2)401 self.assertEqual(cm.msg, m)402 self.assertEqual(cm.args, ('foo',))403 self.assertEqual(cm.kw, dict(bar=1, k=2))404 def test_set_content_default_cm_comes_from_policy(self):405 cm = self._TestContentManager()406 p = policy.default.clone(content_manager=cm)407 m = self._str_msg('', policy=p)408 m.set_content()409 self.assertEqual(cm.msg, m)410 self.assertEqual(cm.args, ())411 self.assertEqual(cm.kw, {})412 m.set_content('foo', bar=1, k=2)413 self.assertEqual(cm.msg, m)414 self.assertEqual(cm.args, ('foo',))415 self.assertEqual(cm.kw, dict(bar=1, k=2))416 # outcome is whether xxx_method should raise ValueError error when called417 # on multipart/subtype. Blank outcome means it depends on xxx (add418 # succeeds, make raises). Note: 'none' means there are content-type419 # headers but payload is None...this happening in practice would be very420 # unusual, so treating it as if there were content seems reasonable.421 # method subtype outcome422 subtype_params = (423 ('related', 'no_content', 'succeeds'),424 ('related', 'none', 'succeeds'),425 ('related', 'plain', 'succeeds'),426 ('related', 'related', ''),427 ('related', 'alternative', 'raises'),428 ('related', 'mixed', 'raises'),429 ('alternative', 'no_content', 'succeeds'),430 ('alternative', 'none', 'succeeds'),431 ('alternative', 'plain', 'succeeds'),432 ('alternative', 'related', 'succeeds'),433 ('alternative', 'alternative', ''),434 ('alternative', 'mixed', 'raises'),435 ('mixed', 'no_content', 'succeeds'),436 ('mixed', 'none', 'succeeds'),437 ('mixed', 'plain', 'succeeds'),438 ('mixed', 'related', 'succeeds'),439 ('mixed', 'alternative', 'succeeds'),440 ('mixed', 'mixed', ''),441 )442 def _make_subtype_test_message(self, subtype):443 m = self.message()444 payload = None445 msg_headers = [446 ('To', 'foo@bar.com'),447 ('From', 'bar@foo.com'),448 ]449 if subtype != 'no_content':450 ('content-shadow', 'Logrus'),451 msg_headers.append(('X-Random-Header', 'Corwin'))452 if subtype == 'text':453 payload = ''454 msg_headers.append(('Content-Type', 'text/plain'))455 m.set_payload('')456 elif subtype != 'no_content':457 payload = []458 msg_headers.append(('Content-Type', 'multipart/' + subtype))459 msg_headers.append(('X-Trump', 'Random'))460 m.set_payload(payload)461 for name, value in msg_headers:462 m[name] = value463 return m, msg_headers, payload464 def _check_disallowed_subtype_raises(self, m, method_name, subtype, method):465 with self.assertRaises(ValueError) as ar:466 getattr(m, method)()467 exc_text = str(ar.exception)468 self.assertIn(subtype, exc_text)469 self.assertIn(method_name, exc_text)470 def _check_make_multipart(self, m, msg_headers, payload):471 count = 0472 for name, value in msg_headers:473 if not name.lower().startswith('content-'):474 self.assertEqual(m[name], value)475 count += 1476 self.assertEqual(len(m), count+1) # +1 for new Content-Type477 part = next(m.iter_parts())478 count = 0479 for name, value in msg_headers:480 if name.lower().startswith('content-'):481 self.assertEqual(part[name], value)482 count += 1483 self.assertEqual(len(part), count)484 self.assertEqual(part.get_payload(), payload)485 def subtype_as_make(self, method, subtype, outcome):486 m, msg_headers, payload = self._make_subtype_test_message(subtype)487 make_method = 'make_' + method488 if outcome in ('', 'raises'):489 self._check_disallowed_subtype_raises(m, method, subtype, make_method)490 return491 getattr(m, make_method)()492 self.assertEqual(m.get_content_maintype(), 'multipart')493 self.assertEqual(m.get_content_subtype(), method)494 if subtype == 'no_content':495 self.assertEqual(len(m.get_payload()), 0)496 self.assertEqual(m.items(),497 msg_headers + [('Content-Type',498 'multipart/'+method)])499 else:500 self.assertEqual(len(m.get_payload()), 1)501 self._check_make_multipart(m, msg_headers, payload)502 def subtype_as_make_with_boundary(self, method, subtype, outcome):503 # Doing all variation is a bit of overkill...504 m = self.message()505 if outcome in ('', 'raises'):506 m['Content-Type'] = 'multipart/' + subtype507 with self.assertRaises(ValueError) as cm:508 getattr(m, 'make_' + method)()509 return510 if subtype == 'plain':511 m['Content-Type'] = 'text/plain'512 elif subtype != 'no_content':513 m['Content-Type'] = 'multipart/' + subtype514 getattr(m, 'make_' + method)(boundary="abc")515 self.assertTrue(m.is_multipart())516 self.assertEqual(m.get_boundary(), 'abc')517 def test_policy_on_part_made_by_make_comes_from_message(self):518 for method in ('make_related', 'make_alternative', 'make_mixed'):519 m = self.message(policy=self.policy.clone(content_manager='foo'))520 m['Content-Type'] = 'text/plain'521 getattr(m, method)()522 self.assertEqual(m.get_payload(0).policy.content_manager, 'foo')523 class _TestSetContentManager:524 def set_content(self, msg, content, *args, **kw):525 msg['Content-Type'] = 'text/plain'526 msg.set_payload(content)527 def subtype_as_add(self, method, subtype, outcome):528 m, msg_headers, payload = self._make_subtype_test_message(subtype)529 cm = self._TestSetContentManager()530 add_method = 'add_attachment' if method=='mixed' else 'add_' + method531 if outcome == 'raises':532 self._check_disallowed_subtype_raises(m, method, subtype, add_method)533 return534 getattr(m, add_method)('test', content_manager=cm)535 self.assertEqual(m.get_content_maintype(), 'multipart')536 self.assertEqual(m.get_content_subtype(), method)537 if method == subtype or subtype == 'no_content':538 self.assertEqual(len(m.get_payload()), 1)539 for name, value in msg_headers:540 self.assertEqual(m[name], value)541 part = m.get_payload()[0]542 else:543 self.assertEqual(len(m.get_payload()), 2)544 self._check_make_multipart(m, msg_headers, payload)545 part = m.get_payload()[1]546 self.assertEqual(part.get_content_type(), 'text/plain')547 self.assertEqual(part.get_payload(), 'test')548 if method=='mixed':549 self.assertEqual(part['Content-Disposition'], 'attachment')550 elif method=='related':551 self.assertEqual(part['Content-Disposition'], 'inline')552 else:553 # Otherwise we don't guess.554 self.assertIsNone(part['Content-Disposition'])555 class _TestSetRaisingContentManager:556 def set_content(self, msg, content, *args, **kw):557 raise Exception('test')558 def test_default_content_manager_for_add_comes_from_policy(self):559 cm = self._TestSetRaisingContentManager()560 m = self.message(policy=self.policy.clone(content_manager=cm))561 for method in ('add_related', 'add_alternative', 'add_attachment'):562 with self.assertRaises(Exception) as ar:563 getattr(m, method)('')564 self.assertEqual(str(ar.exception), 'test')565 def message_as_clear(self, body_parts, attachments, parts, msg):566 m = self._str_msg(msg)567 m.clear()568 self.assertEqual(len(m), 0)569 self.assertEqual(list(m.items()), [])570 self.assertIsNone(m.get_payload())571 self.assertEqual(list(m.iter_parts()), [])572 def message_as_clear_content(self, body_parts, attachments, parts, msg):573 m = self._str_msg(msg)574 expected_headers = [h for h in m.keys()575 if not h.lower().startswith('content-')]576 m.clear_content()577 self.assertEqual(list(m.keys()), expected_headers)578 self.assertIsNone(m.get_payload())579 self.assertEqual(list(m.iter_parts()), [])580 def test_is_attachment(self):581 m = self._make_message()582 self.assertFalse(m.is_attachment)583 m['Content-Disposition'] = 'inline'584 self.assertFalse(m.is_attachment)585 m.replace_header('Content-Disposition', 'attachment')586 self.assertTrue(m.is_attachment)587 m.replace_header('Content-Disposition', 'AtTachMent')588 self.assertTrue(m.is_attachment)589class TestEmailMessage(TestEmailMessageBase, TestEmailBase):590 message = EmailMessage591 def test_set_content_adds_MIME_Version(self):592 m = self._str_msg('')593 cm = self._TestContentManager()594 self.assertNotIn('MIME-Version', m)595 m.set_content(content_manager=cm)596 self.assertEqual(m['MIME-Version'], '1.0')597 class _MIME_Version_adding_CM:598 def set_content(self, msg, *args, **kw):599 msg['MIME-Version'] = '1.0'600 def test_set_content_does_not_duplicate_MIME_Version(self):601 m = self._str_msg('')602 cm = self._MIME_Version_adding_CM()603 self.assertNotIn('MIME-Version', m)604 m.set_content(content_manager=cm)605 self.assertEqual(m['MIME-Version'], '1.0')606class TestMIMEPart(TestEmailMessageBase, TestEmailBase):607 # Doing the full test run here may seem a bit redundant, since the two608 # classes are almost identical. But what if they drift apart? So we do609 # the full tests so that any future drift doesn't introduce bugs.610 message = MIMEPart611 def test_set_content_does_not_add_MIME_Version(self):612 m = self._str_msg('')613 cm = self._TestContentManager()614 self.assertNotIn('MIME-Version', m)615 m.set_content(content_manager=cm)616 self.assertNotIn('MIME-Version', m)617if __name__ == '__main__':...
basecontenteditor.py
Source:basecontenteditor.py
1from zoundry.appframework.global_services import getLoggerService2from zoundry.appframework.ui.events.editcontrolevents import IZEditControlEvents3from zoundry.appframework.ui.events.toolbarevents import ZEVT_TOOLBAR_RESIZE4from zoundry.appframework.ui.util.colorutil import getDefaultDialogBackgroundColor5from zoundry.appframework.ui.widgets.controls.advanced.editcontrol import IZRichTextEditControl6from zoundry.appframework.ui.widgets.controls.common.acceleratortable import ZAcceleratorEntry7from zoundry.appframework.ui.widgets.controls.common.acceleratortable import ZAcceleratorTable8from zoundry.appframework.ui.widgets.controls.common.menu.menu import ZMenu9from zoundry.appframework.ui.widgets.controls.common.menu.menumodel import ZModelBasedMenuContentProvider10from zoundry.appframework.ui.widgets.controls.common.menu.menumodel import ZModelBasedMenuEventHandler11from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbar import ZToolBar12from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbarmodel import ZModelBasedToolBarContentProvider13from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbarmodel import ZModelBasedToolBarEventHandler14from zoundry.appframework.ui.widgets.dialogs.standarddialogs import ZShowNotYetImplementedMessage15from zoundry.base.exceptions import ZAbstractMethodCalledException16from zoundry.blogapp.constants import IZBlogAppAcceleratorIds17from zoundry.blogapp.services.datastore.documentimpl import ZXhtmlContent18from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostEditorToolBarActionContext19from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertImageAction20from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertImgTagAction21from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertLinkAction22from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostSpellCheckAction23from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogRichTextFormatAction24from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZFocusOnTagwordsAction25from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZFocusOnTitleAction26from zoundry.blogapp.ui.editors.blogeditorctrls.blogposteditcontrol import IZBlogPostEditControl27from zoundry.blogapp.ui.editors.blogeditorctrls.metadata import ZBlogPostMetaDataWidget28from zoundry.blogapp.ui.menus.blogeditor.blogcontenteditorcontextmenumodel import ZBlogContentEditorContextMenuModel29from zoundry.blogapp.ui.menus.blogeditor.blogeditortoolbarmodel import ZBlogContentEditorToolbarModel30import wx3132# ------------------------------------------------------------------------------33# Interface that blog content editor controls must implement.34# ------------------------------------------------------------------------------35class IZBlogContentEditorControl:3637 def refreshUI(self):38 u"""refreshUI() -> void39 Updates the UI as well editor content based on the model data40 """ #$NON-NLS-1$41 # end refreshUI()4243 def updateModel(self):44 u"""updateModel() -> void45 Updates the model data (title, xhtml document etc) from the UI controls.46 """ #$NON-NLS-1$47 # end updateModel()4849 def modelSaved(self):50 u"""modelSaved() -> void51 Invoked by the editor framework when the model data has been persisted.52 """ #$NON-NLS-1$53 # end modelSaved()5455# end IZBlogContentEditorControl565758# ------------------------------------------------------------------------------59# Implements the accelerator table for the blog post content editor.60# ------------------------------------------------------------------------------61class ZBlogPostContentEditorAcceleratorTable(ZAcceleratorTable):6263 def __init__(self, context):64 self.context = context65 ZAcceleratorTable.__init__(self, IZBlogAppAcceleratorIds.ZID_BLOG_POST_EDITOR_CONTENT_ACCEL)66 # end __init__()6768 def _createActionContext(self):69 return self.context70 # end _createActionContext()7172 def _loadAdditionalEntries(self):73 return [74 # Bold, Italic, Underline, Strike75 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'B'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_BOLD)), #$NON-NLS-1$76 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'I'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_ITALIC)), #$NON-NLS-1$77 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'U'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_UNDERLINE)), #$NON-NLS-1$78 # create link79 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'L'), ZBlogPostInsertLinkAction()), #$NON-NLS-1$80 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'K'), ZBlogPostInsertLinkAction()), #$NON-NLS-1$81 # insert image82 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'M'), ZBlogPostInsertImageAction()), #$NON-NLS-1$,83 ZAcceleratorEntry(wx.ACCEL_CTRL + wx.ACCEL_SHIFT, ord(u'M'), ZBlogPostInsertImgTagAction()), #$NON-NLS-1$,84 # spell check85 ZAcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F7, ZBlogPostSpellCheckAction()),86 # Go to Title87 ZAcceleratorEntry(wx.ACCEL_ALT, ord(u'T'), ZFocusOnTitleAction()), #$NON-NLS-1$88 # Go to Tagwords89 ZAcceleratorEntry(wx.ACCEL_SHIFT, wx.WXK_TAB, ZFocusOnTagwordsAction()), #$NON-NLS-1$90 ]91 # end _loadAdditionalEntries()9293# end ZBlogPostContentEditorAcceleratorTable949596# ------------------------------------------------------------------------------97# This is the base class of the blog post content area editor.98# Concrete implementations are WsyiWyg and XhtmlText content editors99# ------------------------------------------------------------------------------100class ZBlogPostContentEditorBase(wx.Panel, IZBlogContentEditorControl):101102 def __init__(self, parentWindow, zblogPostEditor, zblogPostEditorModel):103 self.zblogPostEditor = zblogPostEditor104 self.zblogPostEditorModel = zblogPostEditorModel105 self.editor = None106 self.metaDataWidget = None107 self.contentEditCtrl = None108 # dirty: internally keep track if the editor (mshtml/scintilla) content has been modified.109 self.contentModified = False110 wx.Panel.__init__(self, parentWindow, wx.ID_ANY)111 self._createWidgets()112 self._layoutWidgets()113 self._bindWidgetEvents()114 if self._getContentEditControl():115 self._bindContentEditCtrlEvents()116 # end __init__()117118 def focusOnContent(self):119 self.contentEditCtrl.SetFocus()120 # end focusOnContent()121122 def getMetaDataWidget(self):123 return self.metaDataWidget124 # end getMetaDataWidget()125126 def _getModel(self):127 return self.zblogPostEditorModel128 # end _getModel129130 def _getContentEditControl(self):131 return self.contentEditCtrl132 # end _getContentEditControl133134 def _createWidgets(self):135 self.SetBackgroundColour(wx.Colour(255, 255, 255))136 self.metaDataWidget = ZBlogPostMetaDataWidget(self, self._getModel().getMetaDataModel() )137 self.metaDataWidget.SetBackgroundColour(getDefaultDialogBackgroundColor())138 self.contentEditCtrl = self._createContentEditCtrl(self)139 # Note: toolbar must be created only after the contentEditCtrl has been created (so that edit control capabilities can be determined)140 self.toolBar = self._createToolBar()141 self.tbStaticLine = wx.StaticLine(self, wx.ID_ANY)142143 self.acceleratorTable = ZBlogPostContentEditorAcceleratorTable(ZBlogPostEditorToolBarActionContext(self))144 self.contentEditCtrl.SetAcceleratorTable(self.acceleratorTable)145 # end _createWidgets()146147 def _createContentEditCtrl(self, parent):148 # sublcasses must create concrete IZBlogPostEditControl impl.149 raise ZAbstractMethodCalledException(self.__class__.__name__, u"_createContentEditCtrl") #$NON-NLS-1$150 # end _createContentEditCtrl()151152 def _layoutWidgets(self):153 self.sizer = wx.BoxSizer(wx.VERTICAL)154 self.sizer.Add(self.metaDataWidget, 0, wx.EXPAND)155 self.sizer.Add(self.toolBar, 0, wx.EXPAND)156 self.sizer.Add(self.tbStaticLine, 0, wx.EXPAND)157 self.sizer.Add(self.contentEditCtrl, 1, wx.EXPAND)158 self.SetSizer(self.sizer)159 self.SetAutoLayout(True)160 self.Layout()161 # end _layoutWidgets()162163 def _bindWidgetEvents(self):164 self.Bind(ZEVT_TOOLBAR_RESIZE, self.onToolBarResize, self.toolBar)165 wx.EVT_NAVIGATION_KEY(self.metaDataWidget, self.onMetaDataKeyboardNavigation)166 self.acceleratorTable.bindTo(self)167 # end _bindWidgetEvents()168169 def _bindContentEditCtrlEvents(self):170 self.Bind(IZEditControlEvents.ZEVT_UPDATE_UI, self.onUpdateUI, self._getContentEditControl())171 self.Bind(IZEditControlEvents.ZEVT_SELECTION_CHANGE, self.onSelectionChange, self._getContentEditControl())172 self.Bind(IZEditControlEvents.ZEVT_CONTEXT_MENU, self.onContextMenu, self._getContentEditControl())173 self.Bind(IZEditControlEvents.ZEVT_CONTENT_MODIFIED, self.onContentModified, self._getContentEditControl())174 # end _bindContentEditCtrlEvents()175176 def _createToolBar(self):177 self.toolBarModel = self._createToolBarModel()178 self.toolBarContext = ZBlogPostEditorToolBarActionContext(self)179 contentProvider = ZModelBasedToolBarContentProvider(self.toolBarModel, self.toolBarContext)180 eventHandler = ZModelBasedToolBarEventHandler(self.toolBarModel, self.toolBarContext)181 return ZToolBar(contentProvider, eventHandler, self)182 # end _createToolBar()183184 def _createToolBarModel(self):185 toolbarModel = ZBlogContentEditorToolbarModel()186 return toolbarModel187 # end _createToolBarModel()188189 def _isContentModified(self):190 return self.contentModified191 #end _isContentModified()192193 def _setContentModified(self, modified):194 self.contentModified = modified195196 def onUpdateUI(self, event): #@UnusedVariable197 self.zblogPostEditor._fireUpdateMenu() 198 self.toolBar.refresh()199 self._updateCaretPostionUI()200 # end onUIUpdate()201 202 def _updateCaretPostionUI(self):203 (row, col) = self._getContentEditControl().getCaretPosition()204 text = u"" #$NON-NLS-1$205 if row != -1 and col != -1:206 text = u"%d : %d" % (row, col) #$NON-NLS-1$207 self.zblogPostEditor.statusBarModel.setPaneText(u"rowcol", text) #$NON-NLS-1$208 self.zblogPostEditor._fireStatusBarChangedEvent()209 # end _updateCaretPostionUI()210211 def onContentModified(self, event): #@UnusedVariable212 # This is the event handler for mshtml/stc content modified/dirty indicator.213 # Notify the blog post editor just one time214 if not self._isContentModified():215 self.zblogPostEditor.setDirty(True)216 self._setContentModified(True)217 # end onContentModified()218219 def onSelectionChange(self, event): #@UnusedVariable220 pass221 # end onSelectionChange()222223 def onContextMenu(self, event):224 linkCtx = self.getLinkContext()225 imageCtx = self.getImageContext()226 tableCtx = self.getTableContext()227 removeExtMarker = False228 if self.hasCapability(IZBlogPostEditControl.ZCAPABILITY_EXTENDED_ENTRY_MARKER) \229 and self._getContentEditControl().canRemoveExtendedEntryMarker():230 removeExtMarker = True231232 menuModel = ZBlogContentEditorContextMenuModel()233 menuModel.initialize(linkCtx, imageCtx, tableCtx, removeExtMarker)234235 menuContext = self.zblogPostEditor.getMenuActionContext()236 contentProvider = ZModelBasedMenuContentProvider(menuModel, menuContext)237 eventHandler = ZModelBasedMenuEventHandler(menuModel, menuContext)238 menu = ZMenu(event.getParentWindow(), menuModel.getRootNode(), contentProvider, eventHandler)239 try:240 event.getParentWindow().PopupMenu(menu, event.getXYPoint())241 except Exception, e:242 getLoggerService().exception(e)243 menu.Destroy()244 # end onContextMenu()245246 def refreshUI(self):247 document = self.zblogPostEditorModel.getDocument()248 # refresh title, tags, blog data etc.249 self.metaDataWidget.refreshUI()250 # get xhtml content and set it in the edit control.251 if document.getContent() is not None:252 xhtmlDoc = document.getContent().getXhtmlDocument()253 self._getContentEditControl().setXhtmlDocument(xhtmlDoc)254 # end refreshUI()255256 def modelSaved(self):257 # model was saved to data store.258 # Clear dirty flag so that onContentModified() handler can set the flag in the model259 self._setContentModified(False)260 # Clear flags (such as 'content modified') in the content editor (eg. ZMSHTMLBlogPostEditControl via ZBlogPostWysiwygContentEditor)261 self._getContentEditControl().clearState()262 # Refresh widget UI.263 self.metaDataWidget.refreshUI()264 # end modelSaved()265266 def updateModel(self):267 document = self.zblogPostEditorModel.getDocument()268 # Flush title, tags, blog meta data etc. from UI controls to zmetadata model.269 self.metaDataWidget.updateModel()270 # Next copy title, tags etc. from meta data model to the document.271 self.zblogPostEditorModel.getMetaDataModel().updateDocument(document)272273 # Finally, get the Xhtml content from the editor and set it in the document274 xhtmlDoc = self._getContentEditControl().getXhtmlDocument()275 xhtmlContent = ZXhtmlContent()276 xhtmlContent.setXhtmlDocument(xhtmlDoc)277 document.setContent(xhtmlContent)278 # end updateModel()279280 def onTool(self, ctx): #@UnusedVariable281 ZShowNotYetImplementedMessage(self)282 # end onTool()283284 def onToolBarResize(self, event):285 self.Layout()286 event.Skip()287 # end onToolBarResize()288289 # This is a bit hack-y - when tabbing from the tagwords text control, in290 # the forward direction, the focus does not get properly set to the291 # content editor. I'm not really sure where it goes, actually. This292 # method will listen for keyboard nav events in the meta data widget and293 # re-route a forward direction Tab event so that it sets the focus294 # explicitly on the content editor.295 def onMetaDataKeyboardNavigation(self, event):296 focusWidget = self.metaDataWidget.FindFocus()297 if event.GetDirection() and event.IsFromTab() and focusWidget == self.metaDataWidget.tagwordsText:298 self.focusOnContent()299 else:300 event.Skip()301 # end onMetaDataKeyboardNavigation()302303 def hasCapability(self, capabilityId):304 return self._getContentEditControl().getCapabilities().hasCapability(capabilityId)305 # end hasCapability()306307 def canCut(self):308 return self._getContentEditControl().canCut()309 # end canCut()310311 def cut(self):312 self._getContentEditControl().cut()313 # end cut()314315 def canCopy(self):316 return self._getContentEditControl().canCopy()317 # end canCopy()318319 def copy(self):320 self._getContentEditControl().copy()321 # end copy()322323 def canPaste(self, xhtmlFormat):324 if xhtmlFormat:325 return self._getContentEditControl().canPasteXhtml()326 else:327 return self._getContentEditControl().canPaste()328 # end canPaste()329330 def paste(self, xhtmlFormat):331 if xhtmlFormat:332 self._getContentEditControl().pasteXhtml()333 else:334 self._getContentEditControl().paste()335 # end paste()336337 def canInsertXhtml(self):338 return self._getContentEditControl().canInsertXhtml()339 # end canInsertXhtml340341 def insertXhtml(self, xhtmlString): #@UnusedVariable342 self._getContentEditControl().insertXhtml(xhtmlString)343 # end insertXhtml344345 def canSelectAll(self):346 return self._getContentEditControl().canSelectAll()347 # end canSelectAll()348349 def selectAll(self):350 self._getContentEditControl().selectAll()351 # end selectAll()352353 def canUndo(self):354 return self._getContentEditControl().canUndo()355 # end canUndo()356357 def undo(self):358 self._getContentEditControl().undo()359 # end undo()360361 def canRedo(self):362 return self._getContentEditControl().canRedo()363 # end canRedo()364365 def redo(self):366 self._getContentEditControl().redo()367 # end redo()368369 # ---------------------------------------------370 # RichTextEdit formatting commands. Eg. Bold, Italic.371 # ---------------------------------------------372373 def isFormattingEnabled(self, capabilityId):374 return self._getContentEditControl().isFormattingEnabled(capabilityId)375 # end isFormattingEnabled()376377 def getFormattingState(self, capabilityId):378 return self._getContentEditControl().getFormattingState(capabilityId)379 # end getFormattingState()380381 def applyFormatting(self, capabilityId, customData):382 self._getContentEditControl().applyFormatting(capabilityId, customData)383 # end applyFormatting()384385 def getLinkContext(self):386 u"""getLinkContext() -> IZXHTMLEditControlLinkContext387 Returns link creation and edit context if available or None otherwise.""" #$NON-NLS-1$388 return self._getContentEditControl().getLinkContext()389 # end getLinkContext390391 def getImageContext(self):392 u"""getImageContext() -> IZXHTMLEditControlImageContext393 Returns image creation and edit context if available or None otherwise.""" #$NON-NLS-1$394 return self._getContentEditControl().getImageContext()395 # end getImageContext396397 def getTableContext(self):398 u"""getTableContext() -> IZXHTMLEditControlTableContext399 Returns table insertion and edit context if available or None otherwise.""" #$NON-NLS-1$400 return self._getContentEditControl().getTableContext()401 # end getTableContext402403 def getCurrentSelection(self):404 u"""getCurrentSelection() -> IZXHTMLEditControlSelection405 """ #$NON-NLS-1$406 return self._getContentEditControl().getCurrentSelection()407 # end getCurrentSelection()408409 # ---------------------------------------------410 # Extended entry411 # ---------------------------------------------412413 def insertExtendedEntryMarker(self):414 self._getContentEditControl().insertExtendedEntryMarker()415 # end insertExtendedEntryMarker()416417 def removeExtendedEntryMarker(self):418 self._getContentEditControl().removeExtendedEntryMarker()419 # removeExtendedEntryMarker()420421 # ---------------------------------------------422 # Spellcheck423 # ---------------------------------------------424 def createSpellCheckContext(self):425 u"""createSpellCheckContext() -> IZEditControlSpellCheckContext426 Returns IZEditControlSpellCheckContext if spellcheck is supported or None otherwise.427 """ #$NON-NLS-1$428 return self._getContentEditControl().createSpellCheckContext()429 # end createSpellCheckContext()430431 # ---------------------------------------------432 # Find and Find/Replace433 # ---------------------------------------------434 def createFindReplaceContext(self):435 u"""createFindReplaceContext() -> IZEditControlFindReplaceTextContext436 Returns IZEditControlFindReplaceTextContext if find/replace is supported or None otherwise.437 """ #$NON-NLS-1$438 return self._getContentEditControl().createFindReplaceContext()439 # end createFindReplaceContext()440 441 # ---------------------------------------------442 # Validate and Tidy443 # ---------------------------------------------444 def schemaValidate(self):445 self._getContentEditControl().schemaValidate()446 # end schemaValidate447 448 def clearValidation(self):449 self._getContentEditControl().clearValidation()450 # end clearValidation 451 452 def runTidy(self):453 self._getContentEditControl().runTidy()454 # end runTidy 455 456
...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!